WPF app with static DbContext in ViewModel

c# dbcontext entity-framework-6 mvvm wpf

Question

I'm developing WPF application using MVVM, Code-First and the repository pattern. I need to have a background task, which processes webserver requests from clients and saves new data into database.

The problem is each ViewModel has a property (ObservableCollection), which gets Repository.GetObservableCollection(). So each ViewModel has a repository instance, which has the same DbContext (so I don't get DbException when saving complex entities). This DbContext is long-living until the end of app in each repo and is injected from MainViewModel to the contructors of VMs.

When I save new data to the database from the background task, the GUI doesn't update, because I'm using different DbContext there (I have to due to concurrent requests):

using (var db = new DbContextManager())
{
    var client = new Client();
    db.Client.Add(client);
    db.SaveChanges();
}

There are two ways, which I tried:

  1. Set up DispatcherTimer in MainViewModel to update ViewModels every 2secs using ViewModel.Repository.LoadAll for each repo. This lags my UI every 2secs, but it works.
  2. When saving new data into the DB, also add the entities to Repositories via.

    Application.Current.Dispatcher.Invoke ( () => { _clientRepository.Add(client); } );

That way, the entities appear in GUI immidiately, but there's also slight lag (when moving window) and I can't update existing entity's property.

The question is how can I refactor this to allow both GUI and background interaction with entities. How to properly combine repository and MVVM?

1
1
3/25/2017 9:10:16 AM

Popular Answer

Don't directly link a ViewModel entity with a DB DataContext. You need to define an ObservableCollection in the ViewModel and its binding in the XAML.

After that you'll asynchronously update the collection by calling functions from your persistence layer. So you better off with a LoadAsync, alternatively you could wrap your DB retrieval into a Task. This approach will work because await will capture the SynchronizationContext of the UI thread and will update your component in the UI thread.

The actual DataContext has to be hidden in that persistence layer and must remain unknown to the ViewModel.

Reply to comments

if I don't pass DbContext to the ViewModels (which in turn pass it to its Repository), how do I get the context to the repository, so I can call

point void LoadAll() { 
    context.Set<T>().Load(); 
} 

and

public ObservableCollection<T> GetObservableCollection() { 
    return context.Set<T>().Local; 
}

As per my answer, the ViewModel can call the persistence layer function to load the collection, (it's not even necessary that the type returned from the persistence layer is observable). The point is that you won't return void and you'll possibly use a LoadAsync so that you can await it when you assign the returned data to the underlying collection of your ViewModel property.

so I have to use Dispatcher.Invoke to interact with repos/viewmodels

No, don't use Dispatcher.Invoke to interact with the repository DB context. Make the interaction with the DB async. Notice that Dispatcher.Invoke is only for the UI layer.

0
3/26/2017 10:37:14 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow