Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVVM IoC challenge: implement concrete class for this ViewModel factory interface

I am working on a Windows Store app where I want to use MVVM, Unity and IoC. I am wrestling with the creation of ViewModels that wrap a Model object. Various other posts have asked similar questions, but I believe this is a slightly different take.

I am trying to create a ViewModel factory service that I can inject into my ViewModels. The interface to this factory might look like this:

public interface IViewModelFactoryService {
    TViewModel Create<TViewModel, TModel>(TModel domainObject) 
        where TViewModel : ViewModelBase 
        where TModel : DomainObject;
}

The issue is that I am trying to both pass a model object into the ViewModel constructor and inject services as additional parameters. I am also trying to adhere to the principle that the factory should NOT have a reference to the IoC container, so using container.Resolve within the factory isn't an option.

Complicating the matter is that different ViewModels can, of course, require different services. I believe the solution is likely to involve using InjectionFactory (a Unity object which lets you configure a factory for a registered type), but I can't quite seem to get all of the pieces to fit quite right.

Here is what some of the ViewModel constructors might look like that this factory would need to create:

FooViewModel(Foo model)
BarViewModel(Bar model, IViewModelFactoryService factory)
BazViewModel(Baz model, IViewModelFactoryService factory, IRepository repository)

Here is an example of using Unity's InjectionFactory to create factories for two of those ViewModel classes:

container.RegisterType<Func<Bar, BarViewModel>>(new InjectionFactory(
    c => new Func<Bar, BarViewModel>(
        bar => new BarViewModel(bar, 
            c.Resolve<IViewModelFactoryService>()))))

container.RegisterType<Func<Baz, BazViewModel>>(new InjectionFactory(
    c => new Func<Baz, BazViewModel>(
        baz => new BazViewModel(baz, 
            c.Resolve<IViewModelFactoryService>(), 
            c.Resolve<IRepository>()))))

Once those factories are registered with the container, the following code can be used:

barFactory = container.Resolve<Func<Bar, BarViewModel>>();
barViewModel = barFactory(myBar);

bazFactory = container.Resolve<Func<Baz, BazViewModel>>();
bazViewModel = bazFactory(myBaz);

My ultimate goal, of course, is to do the following:

viewModelFactory = container.Resolve<IViewModelFactory>();
barViewModel = viewModelFactory.Create<BarViewModel, Bar>(myBar);
bazViewModel = viewModelFactory.Create<BazViewModel, Baz>(myBaz);

If I could get to that point, I could inject the IViewModelFactoryService wherever I want and be assured that I could create a view model for any type as long as I had access to the model object being wrapped.

I think there must be some way to use the factories for the individual ViewModels to create the concrete implementation of the IViewModelFactoryService I described, but I can't quite put the pieces together in the right way.

I could create a constructor parameter to the concrete IViewModelFactoryService class for every factory for every ViewModel, but that's obviously not desirable. I could do something similar with property injection on the concrete IViewModelFactoryService class, but then I would end up defining separate properties for every ViewModel. Both of those possible avenues are undesirable, because I would have to modify the concrete IViewModelFactoryService class every time I create a new ViewModel.

Perhaps DynamicObject could be of some use here. I have avoided it in WinRT so far because it doesn't appear that you can bind to dynamic properties of a DynamicObject from XAML. But that wouldn't be an issue for the IViewModelFactoryService concrete class.

FYI, until I get this figured out (if I do), I have punted and am creating my ViewModels outside of the container. Basically I'm doing "Poor Man's Dependency Injection". A ViewModel that needs to create a ViewModel gets injected with all of the parameters the new ViewModel might need. For example, if BarViewModel needs to create a BazViewModel:

class BarViewModel {
    IRepository _repository;
    Bar _bar;
    BarViewModel(Bar bar, IRepository repository) {
        _bar = bar;
        _repository = repository;
    }

    void DoSomethingWithBaz(Baz baz) {
        var bazViewModel = new BazViewModel(baz, _repository);
        // do something with bazViewModel
    }

The disadvantage of this, of course, is that Bar itself shouldn't have a dependency on IRepository. It only gets that injected because it is going to need to use it when constructing BazViewModel. The abstract factory model would eliminate the dependency of Bar on IRepository. The other disadvantage is that the container is no longer managing the BazViewModel and I have to do the injection myself.

UPDATE

Here are a few blog posts that led me to lean in favor of keeping the container out of the IViewModelFactoryService concrete class. The upshot is a combination of the Register Release Resolve pattern and the emphasis on Composition Roots to keep the code base clean and avoid "hidden" dependencies on the IoC container in your business logic.

  • Register Resolve Release pattern
  • Using the Inversion of Control container
  • Pulling from the container after initial composition

Of course, if injecting the container into the concrete factory class makes the whole problem go away and keeps me from adding to my headache, perhaps it's reasonable to sacrifice a little purity. But it feels like giving up on finding the elegant solution.

like image 315
David Cater Avatar asked Oct 19 '22 10:10

David Cater


1 Answers

Based on Patrice's comments, I went ahead and created a version of the concrete class that injects the container. It's certainly easy to create that way, and I can buy the argument that it could be considered part of the Composition Root.

Here's the final version of the interface (to support both ViewModels that wrap a DomainObject and ones that don't).

public interface IViewModelFactory {
    TViewModel Create<TViewModel, TModel>(TModel domainObject) where TViewModel : ViewModelBase where TModel : DomainObject;
    TViewModel Create<TViewModel>() where TViewModel : ViewModelBase;
}

And here's the implementation of ViewModelFactory:

public class ViewModelFactory : IViewModelFactory, IDisposable {
    IUnityContainer _container;
    public ViewModelFactory(IUnityContainer container) {
        if (null == container) throw new ArgumentNullException("container");
        _container = container;
    }

    public TViewModel Create<TViewModel, TModel>(TModel domainObject)
        where TViewModel : GalaSoft.MvvmLight.ViewModelBase
        where TModel : DomainObject {
        return _container.Resolve<Func<TModel, TViewModel>>()(domainObject);
    }


    public TViewModel Create<TViewModel>() where TViewModel : GalaSoft.MvvmLight.ViewModelBase {
        return _container.Resolve<TViewModel>();
    }

    public void Dispose() {
        _container = null;
    }
}

I also need to register a factory delegate for each of the ViewModels I want to be able to create with the factory, plus the ViewModelFactory itself. In this example, StackListItemViewModel is the ViewModel that wraps a DomainObject named Stack:

container.RegisterType<Func<Stack, StackListItemViewModel>>(
    new InjectionFactory(c => new Func<Stack, StackListItemViewModel>(
        stack => new StackListItemViewModel(stack, container.Resolve<IRepository>()))));

container.RegisterType<IViewModelFactory, ViewModelFactory>(new ContainerControlledLifetimeManager(), new InjectionConstructor(container));

Note that I don't want to register an instance of the container itself into the container. If I do that it becomes a slippery slope to someone deciding to start using the container as a ServiceLocator. That's why I'm using an InjectionConstructor to pass the container to the ViewModelFactory's constructor.

like image 142
David Cater Avatar answered Oct 22 '22 21:10

David Cater