none
Problem Saving a New Entity Object using Bindings and View Models

    Question

  • Recently, I implemented Binding Expressions and View Models into my application. Now I can not add new records to my database. When I try, the error states that there are two required fields that do not have values, but I can see them when debugging.

    The code worked fine when I was manually setting the values of the entity. With two-way binding is there something else that must be considered?


    MCD ASP.NET

    Thursday, July 04, 2013 12:44 AM

All replies

  • Have you made sure to have the data context of your view (or parent control) set to the ViewModel?

    If you're using Silverlight 5, you can use Binding Statement Debugging to help find errors. Set a breakpoint on the line of the biding in your xaml, and when its hit, you can inspect the binding source and browse any binding errors in the locals window.

    If you're still having difficulty, paste some code and maybe we can spot something.

    Adam

    Thursday, July 04, 2013 2:04 AM
  • Here is the code.

    ===========

    View XAML
    ===========

         <TextBox Canvas.Left="163" Canvas.Top="227" Height="33" Name="txtItemName"
             Text="{Binding ItemName, Mode=TwoWay, ValidatesOnExceptions=True}" Width="264" FontSize="12" />

         <TextBox Canvas.Left="163" Canvas.Top="280" Height="65" Name="txtItemDescription"
             Text="{Binding ItemDescription, Mode=TwoWay}" Width="492" FontSize="12" />

    ===========
    View Code
    ===========

          private void GetData()
          {
              ManageGetListModel getListModel = new ManageGetListModel();
              getListModel.ServiceComplete += new ManageGetListModel.ServiceCompleteEventHandler(getListModel_ServiceComplete);
              getListModel.GetListItemByID(appSettings.GetListID);
          }

          void getListModel_ServiceComplete(object sender, EventArgs e)
          {
              getListModel = (sender as ManageGetListModel);

              if (getListModel != null) LayoutRoot.DataContext = getListModel;
          }

    =================
    View Model Code
    =================

        public class ManageGetListModel: INotifyPropertyChanged
        {
            public delegate void ServiceCompleteEventHandler(object sender, EventArgs e);
            public event ServiceCompleteEventHandler ServiceComplete;

            public delegate void SaveCompleteEventHandler(object sender, EventArgs e);
            public event SaveCompleteEventHandler SaveComplete;
            public event PropertyChangedEventHandler PropertyChanged;

            #region "Variables"

                DomainContext _Context = new DomainContext();
                GetList _getListItem;
                AppSettings appSettings = new AppSettings();
            
            #endregion

            #region "Properties"

                public int? GetListID
                {
                    get
                    {
                        if (_getListItem == null) return null;
                        else return _getListItem.GetListID;
                    }
                }

                public string ItemName
                {
                    get
                    {
                        if (_getListItem == null) return null;
                        else return _getListItem.ItemName;
                    }
                    set
                    {
                        _getListItem.ItemName = value;
                        PropertyChanged(this, new PropertyChangedEventArgs("ItemName"));
                    }
                }

                public string ItemDescription
                {
                    get
                    {
                        if (_getListItem == null) return null;
                        else return _getListItem.ItemDescription;
                    }
                    set
                    {
                        _getListItem.ItemDescription = value;
                        PropertyChanged(this, new PropertyChangedEventArgs("ItemDescription"));
                    }
                }

                public bool HasChanges
                {
                    get
                    {
                        return _Context.HasChanges;
                    }
                }

            #endregion

                #region "Methods"

                public ManageGetListModel()
                {
                    _getListItem = new GetList();
                }

                public void GetListItemByID(int ID)
                {
                    _Context.Load(_Context.GetListItemByIDQuery(ID)).Completed += new EventHandler(ManageGetList_Completed);
                }

                public bool New()
                {
                    _getListItem = new GetList();
                    return true;
                }

                public bool Save()
                {
                    if (_Context.HasChanges)
                    {
                        _getListItem.Modified = DateTime.Now;

                        if (_getListItem.GetListID == 0)
                        {
                            _getListItem.Created = DateTime.Now; // Required
                            _getListItem.UserName = appSettings.UserName; // Required
                            _Context.GetLists.Add(_getListItem);
                        }
                        _Context.SubmitChanges().Completed += new EventHandler(Save_Completed);
                    }
                    return true;
                }

            #endregion

            #region "Event Handlers"

                private void NotifyPropertyChanged(String propertyName="")
                {
                    if (PropertyChanged != null)
                    {
                        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
                    }
                }

                void ManageGetList_Completed(object sender, EventArgs e)
                {
                    _getListItem = (sender as System.ServiceModel.DomainServices.Client.LoadOperation<GetList>).Entities.FirstOrDefault();
                    ServiceComplete(this, EventArgs.Empty);
                }

                void Save_Completed(object sender, EventArgs e)
                {
                    var lo = (sender as System.ServiceModel.DomainServices.Client.SubmitOperation);

                    if (lo != null && lo.HasError)
                    {
                        string x = lo.Error.Message;
                        _Context.GetLists.Remove(_getListItem);                    
                        lo.MarkErrorAsHandled();
                    }

                    SaveComplete(this, EventArgs.Empty);
                }

            #endregion
        }


    MCD ASP.NET


    • Edited by pearsonbe Thursday, July 04, 2013 2:16 PM streamline code
    Thursday, July 04, 2013 2:08 PM
  • If your id is an int?, the default value is null.

    Later, when you Save, you check the id against a value of 0:

    if (_getListItem.GetListID == 0)
    {
        _getListItem.Created = DateTime.Now; // Required
        _getListItem.UserName = appSettings.UserName; // Required
    
        _Context.GetLists.Add(_getListItem);
    }

    This code block won't be set - so your two required fields that are missing are the Created and UserName fields that are missed here.

    Hope that helps,

    Adam

    Thursday, July 04, 2013 3:03 PM
  • When the entity is created the ID is set to 0 (by EF) and this code runs. In the Save_Completed event, I inspect the entities in error and the fields that the debugger displays are ItemName and ItemDescription. I even tried setting all of the entity values except the ID. They are set but the save is not successful.

    MCD ASP.NET


    • Edited by pearsonbe Thursday, July 04, 2013 4:58 PM correction
    Thursday, July 04, 2013 4:21 PM
  • Strange.  I'm running out of ideas.  To be honest, I don't know how or why the _context would even report having changes if you just fetched your data and then created the new one.  When you call New(), you create the new GetList, but it isn't attached to the _context at that point. So when Save() is called, _context.HasChanges seems like it should be false, unless I'm missing something.  I think I would try something like this:

    public bool New()
    {
        _getListItem = new GetList
        {
            Created = DateTime.Now, // Required
            UserName = appSettings.UserName, // Required
        };
        
        _Context.GetLists.Add(_getListItem);
        return true;
    }
    
    public bool Save()
    {
        if (_Context.HasChanges)
        {
            _getListItem.Modified = DateTime.Now;
    
            _Context.SubmitChanges().Completed += new EventHandler(Save_Completed);
        }
        return true;
    }

    But if you were already getting past the HasChanges check before, this shouldn't make a difference.

    Thursday, July 04, 2013 4:53 PM
  • Yup. I tried something similar to your code before and it didn't make a difference. Thanks for looking.

    MCD ASP.NET

    Thursday, July 04, 2013 9:39 PM
  • I wasn't able to solve the problem, but I have a work-around. I changed the binding mode to one-way (read) on the form properties and copy the values from the view to the view model before saving to the database. This is how it worked for reads and writes before adding view models. Not elegant, but it is an improvement over the original design and works until I can I figure out why two-way binding is an issue for new records.

    I may upgrade to SL5 to get the binding statement debugging you mentioned.


    MCD ASP.NET

    Monday, July 08, 2013 5:17 PM