I have searched extensively and have been unable to find any good guidance on how to properly design an application using dependency injection that has multiple layers of view models.

What is the best way to implement dependency injection using something like Unity when you have view models that will need to create view models that will need to create yet other view models?

For example, let's say I have an application with a MainViewModel where I want to be able to show a bunch of different types of data in a tabbed interface a la Visual Studio.

I execute a command to open a collection of User objects using a view model called UsersViewModel. This view model takes a repository IUserRepository in its constructor. A GetAll method on IUserRepository is invoked to get a collection of User objects which are displayed in a grid in the view.

If the details for a specific User object need to be edited, I need to create a UserViewModel that also takes an instance of IUserRepository and the ID of the particular User object in its constructor. A FindById method on IUserRepository is invoked to get the specific User object.

I need to show the User details in a separate tab in the main view. I have a requirement to be able to view/edit the details for multiple User objects simultaneously, so I can't just open the details modally. As such, the UserViewModel needs to be able to persist its own changes since the UsersViewModel could be closed before a particular UserViewModel is saved.

So what is the best way to resolve the UserViewModel instances for this scenario? I could pass an instance of IUnityContainer into UsersViewModel and then use that to resolve them, but from what I've read, that is a bad idea. How else can it be done?

有帮助吗?

解决方案

I have set up a MVVM application with Autofac and MEF to solve the problem you are talking about. To make a long story short, I have created a service interface IViewModelProvider which serves ViewModels for arbitrary models. The implementation of this service provides a cache for all ViewModels and creates ViewModels for models if no ViewModels of the requested ViewModel type for a particular model can be found.

The ViewModelProvider is kind of it's own IoC container, which reflects all loaded assemblies at initialization and then resolves the dependencies at runtime. The ViewModelProvider registers an instance of itself for the service IViewModelProvider and thus can pass itself to the ViewModelswhich require the ability to instantiate new ViewModels.

I am very happy with this solution, as I feel like having a neat separation of concerns here. The global ViewModel cache is awesome, because it saves resources and you can actually compare ViewModels for equality, which I come across quite often.

This solution can be simplified by using the same IoC container as for the UI, as you have pointed in your question. I agree that it is not a good idea to pass the IUnityContainer to the ViewModels directly. I can't see anything wrong about using the container for resolving the ViewModels, though. I would recommend to build a simple service, which caches ViewModel instances and creates new ViewModels by using the IUnityContainer.

The following code is pseudo code, mixing a bit of MEF with a bit of Autofac syntax, but I think it get's clear what it's about:

An interface for getting the ViewModels:

public interface IViewModelProvider
{
    T GetViewModel<T>(object model);
}

An implementation which imports the Unity container and uses it for resolving ViewModels which have not been cached already. The ViewModelCache is something like an advanced dictionary you have to build yourself.

[Export(typeof(IViewModelProvider))]
public class ViewModelProvider : IViewModelProvider
{
    private IUnityContainer _container;

    private ViewModelCache _viewModelCache;

    [ImportingConstructor]
    public ViewModelProvider(IUnityContainer container)
    {
        _container = container;
    }

    public T GetViewModel<T>(object model)
    {
        if (_viewModelCache.Contains<T>(model))
            return _viewModelCache.Get<T>(Model);

        var viewModel = _container.Resolve<T>();
        viewModel.Model = model;

        _viewModelcache.Cache(viewModel);

        return viewModel;         
    }
}

then this service can be injected into your ViewModels and the ViewModels can create their own children ViewModels and don't have to care about whether these already exist because or need to be created.

Hope this helps. It's a broad topic, so please ask if you have any particular questions.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top