none
MVVM: Sharing DbContext between ViewModels RRS feed

  • Question

  • In a WPF, Prism 5, Mvvm desktop app, I have ListView and ListViewModel, from which the user selects an item for editing in the DetailView and DetailViewModel.  The ListViewModel creates the DbContext and selects the records. 

     var UoW = ServiceLocator.Current.GetInstance<EntityUnitOfWork>();
    _itemsList = (from myrow in UoW.AdrTypeRos.Get()
     orderby myrow.desc
     select new AdrTypeMdl()
     {
     adrtype_id = myrow.adrtype_id,
     desc = myrow.desc,
     }).ToList();

    When the user selects a record for editing, EventAggregator publishes and passes the selected object to the DetailViewModel. 

    // Method for SelectionChanged
    private void SelObjChanged(object sender, EventArgs e)
    {// when the current selection changes.
    AdrTypeMdl adrType = ItemsLcv.CurrentItem as AdrTypeMdl;
    
    if (adrType != null)
    {  EventAggregator.GetEvent<SyncDetailEvent>().Publish(adrType);
    }

    My question is where and how to save changes made in DetailView.  It has a TextBox, the text of which is bound to Desc, a property in DetailViewModel. 

     <TextBox>
        <TextBox.Text>
            <Binding Path="Desc" UpdateSourceTrigger="PropertyChanged"  
                     ValidatesOnDataErrors="True">
      </TextBox.Text>       
    </TextBox>
    
    With the EventAggregator & PubSub patterns I am using, how and where should I call the SaveChanges method on my UnitOfWork, in ListViewModel or in DetailViewModel?  If save the changes in DetailViewModel, how do ListView and ListViewModel get notice of the changes saved in DetailViewModel?  ListViewModel publishes to DetailViewModel, but I don't think PubSub allows for two-way communications for DetailViewModel to Publish back to ListViewModel, or does it?  Maybe I need to create a public Property in ListViewModel that binds two-way to DetailViewModel?  Any ideas?  Thank you.

    Friday, December 11, 2015 6:04 AM

Answers

  • >>With the EventAggregator & PubSub patterns I am using, how and where should I call the SaveChanges method on my UnitOfWork, in ListViewModel or in DetailViewModel?

    The DetailsViewModel should issue the save command but if you store the DbContext in the ListViewModel you could send a "save" event using the event aggregator from the DetailsViewModel to the ListViewModel and call the Save() method of your UnitOfWork/DbContext in the ListViewModel.

    Another option is expose the DbContext/UnitOfWork from a static property of an ApplicationService class that both view models can access, .e.g.:

    public class ApplicationService
    {
    public static UnitOfWork
    {
    get { return _unitOfWork; }
    }
    }

    >>If save the changes in DetailViewModel, how do ListView and ListViewModel get notice of the changes saved in DetailViewModel?

    For example using the event aggregator.

    >>ListViewModel publishes to DetailViewModel, but I don't think PubSub allows for two-way communications for DetailViewModel to Publish back to ListViewModel, or does it?

    No, but it is not a two-way communication. It is two separate one-way communications. One event is used to display the details and the other one is used to save the them. The details event is published by the ListViewModel and handled by the DetailViewModel and the save event is published by the DetailViewModel and handled by the ListViewModel.

    The beauty of using the event aggregator pattern is that there is no coupling between the publisher and the subscriber, i.e. the publisher doesn't know anything about the subscriber. It just sends a message to the outside world, for anyone who is interested to handle. Please refer to my blog post for more information: http://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/


    Hope that helps.


    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Marked as answer by Harpagornis Friday, December 11, 2015 5:09 PM
    Friday, December 11, 2015 4:25 PM

All replies

  • The DetailVM is a wrapper for a ListModel-Item?

    If so, you could (you also should) use Commands on the ListVM to save changes. You could bind the ListItemTemplate Click or whatever (i don't wanna look up whats available now, you have to do that on your own, sorry) to the Command which would be a Property on the ViewModel. (for a button there is a Command DP and a CommandParameter DP, the latter would need to be bound to the Current Selected Item (if you bind on the ListView instead of the Item itself) or to the Item from which the Command would be invoked from (if your ListitemTemplate has a button on it).

    I don't know if you Context is recreated every time or not.

    Either case it would work, you would just need to merge the new data of the DetailVM with the data from the Database. You can use the context.Entry<X>(context.Find<X>(DetailModel)).CurrentValues.Set(DetailModel) (The method may have a different name or is accessible on the same level as the CurrentValues property) to merge. Note that this method does NOT take NavigationProperties into account, you'll have to implement a logik for that on your own. Note also that it might be best to use the primary key values with the Find-Method instead of the object.

    If you want the Item changed whenever you type something in there, I can only tell you it is possible, but I would strongly recommend you to not do that (it just involves to much traffic and validation that way). Also EF (if you use it) is disconnected for a reason, you should try to use it as disconnected (meaning implement a save button, not live updating).

    I also want to add this: please step away from Events, WPF is not EventDriven and using those instead of Commands breaks the pattern.


    Please be so kind to close your Threads when you found an answer, these Threads should help everyone with similar issues.
    You can close a Thread via the"Mark as Answer" link below posts. You can mark your own posts as answers if you were not helped out but found a solution, in such a case, please provide the answer.
    Happy coding
    PS: I assure everyone that I did not ever had the desire to offend anyone.






    • Edited by MDeero Friday, December 11, 2015 8:50 AM
    Friday, December 11, 2015 8:40 AM
  • OK Thank you.  There is a Save button in DetailViewModel but no buttons in ListView.

    When you say, "merge the new data of the DetailVM with the data from the Database," how do I pass the changed record object from DetailViewModel to ListViewModel so that ListViewModel can call its SaveChanges on the Unit of Work?   Then, does ListViewModel do a Find as you said, and loop through each field replacing and updating each of the fields in the changed record, or would it be faster and easier to just do a delete and add?


    Friday, December 11, 2015 3:01 PM
  • >>With the EventAggregator & PubSub patterns I am using, how and where should I call the SaveChanges method on my UnitOfWork, in ListViewModel or in DetailViewModel?

    The DetailsViewModel should issue the save command but if you store the DbContext in the ListViewModel you could send a "save" event using the event aggregator from the DetailsViewModel to the ListViewModel and call the Save() method of your UnitOfWork/DbContext in the ListViewModel.

    Another option is expose the DbContext/UnitOfWork from a static property of an ApplicationService class that both view models can access, .e.g.:

    public class ApplicationService
    {
    public static UnitOfWork
    {
    get { return _unitOfWork; }
    }
    }

    >>If save the changes in DetailViewModel, how do ListView and ListViewModel get notice of the changes saved in DetailViewModel?

    For example using the event aggregator.

    >>ListViewModel publishes to DetailViewModel, but I don't think PubSub allows for two-way communications for DetailViewModel to Publish back to ListViewModel, or does it?

    No, but it is not a two-way communication. It is two separate one-way communications. One event is used to display the details and the other one is used to save the them. The details event is published by the ListViewModel and handled by the DetailViewModel and the save event is published by the DetailViewModel and handled by the ListViewModel.

    The beauty of using the event aggregator pattern is that there is no coupling between the publisher and the subscriber, i.e. the publisher doesn't know anything about the subscriber. It just sends a message to the outside world, for anyone who is interested to handle. Please refer to my blog post for more information: http://blog.magnusmontin.net/2014/02/28/using-the-event-aggregator-pattern-to-communicate-between-view-models/


    Hope that helps.


    Please remember to close your threads by marking helpful posts as answer and then start a new thread if you have a new question. Please don't ask several questions in the same thread.

    • Marked as answer by Harpagornis Friday, December 11, 2015 5:09 PM
    Friday, December 11, 2015 4:25 PM
  • So if, as you say, I expose the DbContext/UnitOfWork from a static property of an ApplicationService class that both view models can access.  Then when one of the viewmodels calls DbContext.SaveChanges, does that automatically get published to all the viewmodels, or do I need to add a publish event in the static class, with all viewmodels subscribing, or how does that work?  Thank you again.
    Friday, December 11, 2015 5:54 PM
  • >>Then when one of the viewmodels calls DbContext.SaveChanges, does that automatically get published to all the viewmodels, or do I need to add a publish event in the static class, with all viewmodels subscribing, or how does that work? 

    Calling SaveChanges just persists the changes to the database. You will still need to provide and subscribe to change notifications in order to update any existing views dynamically. The way to do this is to implement the INotifyPropertyChanged interface: https://msdn.microsoft.com/library/ms743695(v=vs.100).aspx

    But please start a new thread if you have a new question.

    Hope that helps.

    Please remember to mark all helpful posts as answer and please don't ask several questions in the same thread.

    Friday, December 11, 2015 6:06 PM
  • Will do.  Thank you again.
    Friday, December 11, 2015 8:03 PM