2

Context: Using Unity in (C#) ASP.NET MVC3 framework.

Mark Seemann suggests "In ASP.NET MVC applications it’s global.asax and a custom IControllerFactory" - (Source).

I have read other credible sources suggest to use the UnityDependencyResolver (MSDN, Adam Tuliper, Darin Dimitrov).

Darin also suggests that the two are mutually exclusive.

Which way is best practice?

Community
  • 1
  • 1
Travis J
  • 81,153
  • 41
  • 202
  • 273

1 Answers1

4

MVC 3 introduced a new way to handle Dependency Injection called IDependencyResolver. In MVC prior to MVC 3, you would use IControllerFactory. In MVC 3 you would use UnityDependencyResolver.

UnityDependencyResolver is an implementation of IDependencyResovler interface. This allows Unity to be integrated into .net without having to write a custom IControllerFactory.

They are mutually exclusive, in that if you use one you would not use the other.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • 1
    Thank you for the response. Mark goes on to say of IDependencyResolver "On a conceptual level, the intended use of IDependencyResolver is as a SERVICE LOCATOR, and that is how the framework uses it." With regards to the suggestion of IDependencyResolver he directly states to avoid it "In its current incarnation, I find it safer and more correct to ignore IDependencyResolver.", and to use an IControllerFactory. So, is your answer implying that Unity can only be used as a Service Locator, or is it possible to use Unity with a custom IControllerFactory? – Travis J Feb 06 '12 at 22:47
  • 1
    @TravisJ - I'm not familiar with his reasons, but i'm sure he has good ones. Still, DependencyResolver is just fine for most people. IDependencyResolver is used throughout the framework, not just for controllers. Not using IDependencyResolver means having to do a lot more work. Yes, you could use them together in so much as your own IControllerFactory would not use DependencyResolver, but other parts of the framework would. I still would only use a custom IControllerFactory if I actually need it. You also run the risk of have multiple containers, which could be bad. – Erik Funkenbusch Feb 06 '12 at 23:21
  • I was hoping to see a stronger argument for the Dependency Resolver. What would be using IDepencyResolver in the basic mvc3 skeleton (i.e. a new "internet" project)? I do not want to use both at the same time, in fact, I am trying to justify using the dependency resolver instead of a custom IControllerFactory but just cannot see any reason to as it is not best practice. I do *not* want to implement a service locator, which is what IDependencyResolver is designed to do. – Travis J Feb 07 '12 at 01:34
  • @TravisJ - IDependencyResolver is not just a service locator. It resolves all dependencies. The nice thing about IDependencyResolver is that it's transparent. It just magically works behind the scenes. You setup your DI mappings and then you just add constructor and/or property arguments and they just automagically appear with the configured data without you having to call into the DI Container yourself. Personally, I use Ninject, and have never had an issue with IDependencyResolver. However, I don't typically do anything fancy with it. – Erik Funkenbusch Feb 07 '12 at 05:54
  • @TravisJ - I understand that you feel that Mark is an authority, and respect his opinion, but thousands of apps are written with IDependencyResolver. Mark is being a perfectionist, IMO. It is a best best practice to use IDependencyResolver, unless there is some specific reason not to. When someone says that Service Location is an anti-pattern, they're talking about using Service Location in place of constructor or property injection. So don't use IDependencyResolver that way. – Erik Funkenbusch Feb 07 '12 at 05:56
  • 1
    Not to be a perfectionist, but apart from the claim that it's 'best practice', what's the benefit of using IDependencyResolver? To me it simply seems to be a more complicated way of doing something that can be achieved a lot simpler with IControllerFactory. – Mark Seemann Feb 07 '12 at 08:29
  • @MarkSeemann - the benefit is that IDependencyResolver is used internally by the framework in more places than just the Controller factory. And I don't really see it as being all that complicated, as you're just implementing a rather simple interface. Most DI frameworks now come with IDR support built-in. – Erik Funkenbusch Feb 07 '12 at 16:38
  • 2
    What the framework does internally shouldn't be an argument for or against. The arguments should be based on whether or not one or the other option adds benefits to one's own code. For DependencyResolver I don't see that happening - in fact, it's quite insidious: http://stackoverflow.com/a/5654204/126014 – Mark Seemann Feb 07 '12 at 16:56
  • @MarkSeemann - I really don't understand your argument. Using IDR allows the framework to use whatever DI framework you choose, otherwise it will use it's own internal (non-DI) code. It's an extension mechanism that allows you to customize how the framework resolves dependencies. Not JUST for Controllers, but other types internally (ie, you can configure how other objects get instantiated as well). All DI framworks are service locators at some point. – Erik Funkenbusch Feb 07 '12 at 17:10
  • @MarkSeemann - I have only ever used IDependencyResolver as a service locator a couple of times (where there wasn't any other choice). 99.9% of the time, i'm just allowing the framework to use IDR to inject dependencies into my controllers and viewpages. I never have to call GetService. – Erik Funkenbusch Feb 07 '12 at 17:36
  • 3
    The IDependencyResolver interface is broken because it has no Release mechanism. Using it can lead to memory leaks which are impossible to fix. – Mark Seemann Feb 07 '12 at 17:38
  • @MarkSeemann - What do you mean no release mechanism? Your dependency framework handles the lifetimes of your object. For instance, when using Ninject, I set the lifetimes to that of a single request, or transient. – Erik Funkenbusch Feb 07 '12 at 18:06
  • And what happens in Ninject when the dependency implements IDisposable and holds unmanaged resources? – Mark Seemann Feb 07 '12 at 18:46
  • @MarkSeemann - It calls Dispose, like any other DI container should. – Erik Funkenbusch Feb 07 '12 at 18:53
  • 1
    How does it know when to do that? – Mark Seemann Feb 07 '12 at 19:01
  • @MarkSeemann - the same way anything else does. When it tears down the request, it checks to see if each object that has request or transient lifetimes implements IDisposable, if it does, it calls Dispose(). Worst case scenario, when the object is garbage collected it calls dispose. I See you're on the Hadlow train on this, and I vehemently disagree with him, as does the ASP.NET team. If there is a real issue with Windosor when using IDR, then it's a windsor problem, other containers handle things just fine. – Erik Funkenbusch Feb 07 '12 at 19:07
  • 1
    Well, "other containers" don't. Neither StructureMap, Spring.NET, nor Unity does this either. You can't generalize from what Ninject does. – Mark Seemann Feb 07 '12 at 19:52
  • @MarkSeemann - You can certainly do this with Unity, http://www.neovolve.com/post/2010/06/18/Unity-Extension-For-Disposing-Build-Trees-On-TearDown.aspx Also, StructureMap has HttpContextBuildPolicy.DisposeAndClearAll(); – Erik Funkenbusch Feb 07 '12 at 20:07
  • @MarkSeemann - I guess in the newer version it's called ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects(); – Erik Funkenbusch Feb 07 '12 at 20:19
  • @MarkSeemann - what it boils down to is that the the DI container you use is responsible for object lifetime, and providing a way to release objects. There are ways to do this in pretty much any container, it would surprise me if Windsor can't (in fact, various comments to Hadlow have pointed this out, but he does some hand waving about how you shouldn't have to rely on the container to dispose of itself, and I completely disagree with that. – Erik Funkenbusch Feb 07 '12 at 20:37
  • @MystereMan - "Unfortunately the TearDown method has no affect by default as none of the “out of the box” strategies dispose created instances." (your link) That link shows that hundreds of lines of code had to be written in order to hack a solution together to handle disposing of the service locator and its parts. It seems to me it is not best practice to continue to hack one solution in after another to accomplish a task. How is this not becoming highly coupled? If one thing changes in a Unity versioning then that whole extension could become invalid. – Travis J Feb 07 '12 at 22:08
  • @TravisJ - If Unity changes, you may have to change many things. That's not an excuse. Yes, obviously the extension is highly coupled to Unity, but it's not highly coupled to IDependencyResolver or MVC. There's also a plug-in solution for Unity in MVC http://www.devtrends.co.uk/blog/introducing-the-unity.mvc3-nuget-package-to-reconcile-mvc3-unity-and-idisposable – Erik Funkenbusch Feb 07 '12 at 22:39
  • @MystereMan - "I never have to call GetService" (you), are you injecting units of work into your controller? How are you avoiding having to use the Service Locator as a Service Locator when you need to access, calculate, and manipulate data from a repository? Do you still use the `new` keyword to instantiate DAL objects to work with? – Travis J Feb 07 '12 at 23:44
  • @TravisJ - No. My controllers take constructor arguments for my repositories. MVC creates the controler and resolves all dependencies via the configured DI Container. I never call new, or GetService or anything else, it's all done by the framework. – Erik Funkenbusch Feb 08 '12 at 01:13
  • If you only use it for repositories, then why use a DI container at all - it seems like it would just add a ton of overhead to save you from typing `using(context)` a couple times? How do you resolve interfaces in your actions? – Travis J Feb 08 '12 at 20:47
  • @TravisJ You don't need to resolve interfaces in your actions. they are injected into your controllers constructor. Your comment about "using(context)" ignores testability and creates dependencies in your controllers. I really have no idea what you are referring to about resolving interfaces in your actions. – Erik Funkenbusch Feb 08 '12 at 20:53
  • So you inject every object every action in the controller would need and then hold them all in memory until they are requested? – Travis J Feb 08 '12 at 20:55
  • @TravisJ - No, Controllers are created when the request comes in, and destroyed when the request ends. They're kept in memory, but a "request" is typically only a few seconds at most in length. – Erik Funkenbusch Feb 08 '12 at 21:31
  • @MystereMan - So when a request is made, is the controller injected with all dependencies, or only ones currently relevant. For example, an action needs to fill a view model with all people from the serverRepository. Would patronRepository (or other controller dependencies) still be instantiated if the action does not use it solely because the controller uses it somewhere? – Travis J Feb 08 '12 at 21:53
  • 1
    @TravisJ - If that's an issue, then your controller has more responsibilities than it should have. The Single Responsibility Princple suggest that if your controller has many different kinds of dependencies, you should probably break it into other controllers. Still, even if you have dependencies instantiated that you don't need, in one method or another, those dependencies should be designed in such a way that they don't consume many resources. For instance, your repository should open a database connection when you get data, not when the repository is created. – Erik Funkenbusch Feb 09 '12 at 04:18
  • 1
    @TravisJ - I'm not sure what alternative you expect to occur, you wouldn't inject all your resources into the method, that's ridiculous. How exactly would you expect resources to be injected? – Erik Funkenbusch Feb 09 '12 at 04:21
  • How would I expect resources to be injected? Good question. I would expect my dependencies to be resolved when the initial request is made. I would expect when the request is made ~/Cont/Act that the relevant controller be instantiated. I would expect that upon controller instantiation the dependencies required only by the requested action were then instantiated. I would expect to be able to use those dependencies in the given action. I would expect when the action returns that all objects would be garbage collected. – Travis J Feb 09 '12 at 20:49
  • @TravisJ - and how exactly does the DI controller know which dependencies to inject for which action method? That seems like a very complex set of configurations to manage. Do you have any examples of doing this anywhere else? – Erik Funkenbusch Feb 09 '12 at 21:09
  • @MystereMan - You asked what I would expect :) What really happens is that the container is held as a singleton. The container is instantiated in the Application_Start method when a Session is started and held globally. During this instantiation, all dependencies are registered, and any resolved objects are then held globally for the lifetime of the Session. When a request is made, a controller is instantiated. This instantiation is overloaded, and causes the container to resolve relevant dependencies to the controller, and then pass them to the controller constructor. – Travis J Feb 09 '12 at 21:45
  • The method from the request then uses the dependencies in some fashion. Upon completion, depending on the registration, the dependencies are then garbage collected. However, what is used versus what is available are two different things. Too much is available because the lifetime of the dependencies managed through IDependencyResolver are defined through a best-fit definition; in addition, some dependencies are not used at all but are still instantiated and possibly held in memory after the request depending on the scenario (async controller calls for example). – Travis J Feb 09 '12 at 21:47
  • @MystereMan - "how does the DI container know?": This is a real issue, because the DI Container is the one managing object lifetimes. It would be nice if the controller could sift the instantiated dependencies on request. It would also be nice if the controller could manage the lifetimes. These two aspects are the selling points for me to have a ControllerFactory instead of an IDependencyResolver. – Travis J Feb 09 '12 at 21:54
  • @MystereMan - One way to accomplish what I expected would be to use a controller factory which implements IControllerActivator and IViewPageActivator. The create methods could manage object lifetime. ControllerActivator could resolve all objects requested by the action (stored in HttpContext), and then IViewPageActivator could release all the objects resolved. – Travis J Feb 10 '12 at 00:09
  • @TravisJ - So, you don't have any examples of this in use then? It sounds like a complex process, crossing multiple interfaces with nebulous benefits and out of control over-engineering. It seems you would have complex dependency logic, which sort of defeats the purpose of dependency injection in the first place. The purpose is to make things like testing easier, that is not the case when you have to create these monstrous logic trees in multiple interfaces, just to keep from instantiating something you might not use. I have better things to do with my time, thanks. – Erik Funkenbusch Feb 10 '12 at 05:09
  • @MystereMan - Nope, no examples. I am a strong believer of design patterns and as such sometimes spend perhaps too much time designing. Sorry if I am over analytical, but I am just trying to look at this from all angles. I really do appreciate your answer, comments, and well-informed point of view. Thank you for your time. – Travis J Feb 10 '12 at 19:18
  • @TrivisJ - Here's another example of how it's really the containers responsibility. Ninject 3 will introduce the concept of Factories and Lazy instantiation. This way, the objects are not created until they are actually used. The only overhead is some simple factory or proxy classes. See http://www.planetgeek.ch/2011/12/31/ninject-extensions-factory-introduction/ . In my opinion, what makes IDR so useful is the fact that it leaves all this up to the container, and does not require the application to manage it. If your container is lacking.. don't blame IDR. – Erik Funkenbusch Feb 12 '12 at 18:04
  • @MarkSeemann - See comment above about Factory and Lazy instantiation. I really think you're giving IDependencyResolver a bad rap, because you expect it to do things that are not its responsibility. – Erik Funkenbusch Feb 12 '12 at 18:05