I've looked into this quite a bit and haven't found a "perfect" solution. The repository pattern works wonderfully for MVC applications where the context is short lived because it exists in a short lived controller, but the problem happens when you try to apply that same structure to a wpf app where the VM can persist for long periods of time.
I have used this solution in the past which is much more simple than many of the repo patterns I have seen that attempt to abstract things out to an extreme amount, resulting in near unreadable amounts of code that are difficult to debug. Here are the steps...
- Create a separate project for the EDMX to act as your Data access layer
- Create a "Repositories" folder under the same project
Create a base class "BaseRepository" to act as the "Unit of Work". IDisposable
will allow you to use this in a using(){}
and the partial
will allow you do implement other repositories
public partial class MyEntityRepository : IDisposable
{
MyEntities context = new MyEntities();
public void Dispose()
{
context.Dispose();
}
}
Create another file called "MyOtherRepository". create the same partial class but implement methods based on what you want that file to contain
public partial class MyEntityRepository
{
public void MyOtherMethodSave(EntityObject obj)
{
//work with context
...
context.SaveChanges();
}
}
Now in your VM you can do this...
using(MyEntityRepository repo = new MyEntityRepository())
{
repo.MyOtherMethodSave(objectToSave);
}
This groups all your repositories under one class so you don't have to deal with separate context. It allows you better manage different repos by grouping the methods into different files and helps prevent code duplication. On top of that, your contexts are as short lived as they were without using this pattern.
The disadvantage is that with larger systems, you may have a lot of methods that get bundled under your repo. One solution in that case would be to implement some basic common commands like "Find" or "Add", and implement specialized ones in their respective repository.
I think its bad practice to have a God ViewModel, the topmost ViewModel, let's call it MainWindowViewModel
, should only hold data and behavior coupled to that of the MainWindow, which usually is very thin, all other work should be delegated to submodels.
If you have a docking manager or other window manager logic that code should reside in the MainWindowViewModel
.
Here is a example of a ViewModel I've made that delegates the behavior to sub models
https://github.com/AndersMalmgren/FreePIE/blob/master/FreePIE.GUI/Shells/MainShellViewModel.cs
I do not think it's good practice to pass around data to sub models, it creates dependencies between models. It's better to use a Event aggregation pattern, this way all models interested in data change can subscribe to the event.
Update: I did an improvement to my Main window model, instead of the constructor taking all sub view models it now takes a collection of viewmodels. And then I configure the IoC to inject correct models
private void ConfigurePanels()
{
kernel.Bind<PanelViewModel>().To<ConsoleViewModel>();
kernel.Bind<PanelViewModel>().To<ErrorsViewModel>();
kernel.Bind<PanelViewModel>().To<WatchesViewModel>();
}
This makes the main model a little less coupled to the sub models, before change
public MainShellViewModel(ConsoleViewModel consoleViewModel,
ErrorsViewModel errorsViewModel,
WatchesViewModel watchesViewModel
)
: base(resultFactory)
{
Tools = new BindableCollection<PanelViewModel> {consoleViewModel, errorsViewModel, watchesViewModel};
}
and after
public MainShellViewModel(IEnumerable<PanelViewModel> panels)
: base(resultFactory)
{
Tools = new BindableCollection<PanelViewModel> (panels);
}
https://github.com/AndersMalmgren/FreePIE/commit/1102292d35a26d7693593266042cde9d9f3b4c89
Best Answer
I think a lot of programmers first try to take the shortcut of binding directly to the model, but in my experience this has some major drawbacks. The primary problem is that if your entity model is persisted by NHibernate or similar, then as soon as the View updates the model property, then NHibernate could persist those changes to the database. That doesn't work well for edit-screens that have a Save/Cancel button. In actual fact, it may choose to wait and persist everything as a batch, but the idea is that when you change the model, you're committing your change.
So, you could still get away with binding directly to model properties on read-only screens, but then you're going to have an inconsistency.
Additionally, most models don't implement
INotifyPropertyChanged
so they may not be suitable binding targets if the state of the screen changes after the initial display.Given the ease of auto-properties, I suggest always binding the View to the ViewModel, not to the Model. It's consistent, simple, and gives you the most flexibility to support changes in the future.