Reputation: 3269
I have DI implemented using the constructor pattern in our MVC4 application, it's been working perfectly for a couple of years now.
Recently, we had a contractor come in the do some work. He needed to access our services layer in a view model and hard coded the services (and their repositories) in the constructor of the viewModel, bad, bad, bad.
What he did was basically mess up our loose coupling we worked our buts off to implement and maintain.
Timing right now prevents us from re-writing his code at this time.
Our ViewModels are in a separate assembly/project than the MVC application, as are our services layer and our repository layer.
So far so good.
How can i used dependency injection to resolve my services inside a view model.
I say again, INSIDE A VIEW MODEL, NOT INSIDE THE CONTROLLER!! Doesn't matter to me if it uses the property injection model or constructor injection model.
Thanks in advance for any assistance.
Upvotes: 0
Views: 1208
Reputation: 19171
This is just plain hard. As you know, in fact you stated: at the simplest level IoC and DI using something like Autofac is about constructor injection or property injection. This injection starts at the top of the tree, and happens all the way down the dependency chain through the constructors themselves (C requires V, so IV is injected; V requires R so IR is injected etc.).
Constructor and Property injections are basically the same thing. The difference is usually just about parameter bloat in ctors (I say, and emphasise, usually). I also assume you don't want to use a service locator, due to all the hard work you have put in to only use injection.
The easy part is therefore registering the VM types with Autofac to allow you to inject into the VM ctor or properties. The hard part is then instantiating VMs in the classes (controllers or otherwise) where you create the VMs, without hardcoding in Autofac references, and in such a way that you maintain your testability.
However I therefore can't see how you can get away from doing some minor change to your Controller's constructors (specifically the classes that create the ViewModels if these are not the Controllers): they need either a template ViewModel injected through the constructor (eugh) for you to then pad out with actual values, or they need a "factory" or "generator" injected to allow them to create viewmodels which are in turn injected. There are other things you would have to do to get this to work: it is just messy.
Could you use the "A needs to create instance of B" relationship type, i.e. inject a Func<TViewModel>
into the relevant methods in your instantiators of viewmodels to allow you to create when you have to (also allowing you to create many VMs in the case of a List<TViewModel>
). This will inject services into the VM ctor without you knowing about the injection.
The Dynamic Instantiation "Meaning" here would involve minimal changes to the constructors of your actual controllers, and would retain the same lifetime scopes as the services your controller uses.
Note also that Autofac lets you inject parameters into Controller Action methods using the ExtensibleActionInvoker
as an IActionInvoker
so if you end up with nasty Controller ctor bloat with any of this, you could inject the Func<TViewModel>
into the Action instead, as a last resort.
It's either this or don't change your controller interface (including the constructor), but resort to a Service Locator using Autofac to create ViewModels with the dependencies injected within the methods where you need to instantiate ViewModels.
None of this answers the other problem: what about a ViewModel that is passed back from the client into the Post Action as a parameter. It is possible that registering your ViewModel types with Autofac will take care of that for you, but I have never had the need to try it and test it. If it doesn't you will have to use your template VM, or VM Func
, with dependencies injected, and then copy the actual VM properties over to the template.
Upvotes: 3