Thursday, July 18, 2013

Getting to know MEF - Meet the DefaultDependencyResolver

When I started working for my current employer, the system we were working on was an ASP.NET MVC application. I was new to C#, new to ASP.NET, and never heard of the MVC framework ("MVC? You mean the architecture of...?").

8 months later, I wouldn't say I got it all, but one thing I did get to know which was also interesting was the MEF Dependency Injection framework. MEF 2 is just coming out and it's too early for me to discuss even the first version. What I want to write about today is Dependency Injection. I will review the Dependency Resolver that ships with MVC by default in order to illustrate how simple (read: useless) it is. This will be a good starting point on why MEF is useful.

So what is dependency injection?

The best description for Dependency Injection (DI) I've found so far appears on the Google Guice project description:
Put simply, Guice alleviates the need for factories and the use of new in your Java code. Think of Guice's @Inject as the new new. You will still need to write factories in some cases, but your code will not depend directly on them. Your code will be easier to change, unit test and reuse in other contexts.
Guice's Motivation page elaborates on what DI is and how an API for it would look. Whether you'll be using Guice, Ninject, Autofac, Castle Windsor or MEF - basically it's the same thing all over again. (Sorry, rival developers of different DI implementations!)

Martin Fowler writes about Dependency Injection too, and gives an example of moving an object's dependencies into the container, then using some "container" (more on those later) to supply the dependencies and construct the object.

So, when do we program without using new? In ASP.NET MVC, the most common use-case is when our application is online and someone enters some controller's action. When an incoming request is handled, a new constructor instance must be created. We're not the one writing the code for it - MVC does it "by itself". And if we're complicating our controller's constructor by adding dependencies for it (such as a database context, a provider or a repository class), MVC still has to create it somehow...

MVC creates the controllers?

Yep. And I can prove it to you.

Start Visual Studio (2012 in my case), and start a new C# Project from the "ASP.NET MVC 3 Web Application" template. You'll get the example project with a HomeController. Add a default constructor (that is, a parameterless constructor) to the controller, put a breakpoint on its end and debug the application. When the breakpoint is reached (quite immediately), examine the Call Stack. If you're only seeing 2 calls, right click inside the Call Stack window and enable the "Show External Code" feature.

I'm posting the entire call stack to make one clear point here: although it's looks like lots and lots of details, it can be decyphered. It took me a long time to stop seeing this as "details" and start to actually make things out of it. This was accomplished thanks to two people: my boss, and the author of JustDecompile, the C# Decompiler that everybody love-love-loves.

Below is what you will see. The colors were, of course, added by me:

> MvcApplication2.dll!MvcApplication2.Controllers.HomeController.HomeController() Line 15 C#
  [Native to Managed Transition] 
  [Managed to Native Transition] 
  mscorlib.dll!System.RuntimeType.CreateInstanceSlow(bool publicOnly, bool skipCheckThis, bool fillCache, ref System.Threading.StackCrawlMark stackMark) + 0x72 bytes 
  mscorlib.dll!System.Activator.CreateInstance(System.Type type, bool nonPublic) + 0x54 bytes 
  mscorlib.dll!System.Activator.CreateInstance(System.Type type) + 0x7 bytes 
  System.Web.Mvc.dll!System.Web.Mvc.DependencyResolver.DefaultDependencyResolver.GetService(System.Type serviceType) + 0x1c bytes 
  System.Web.Mvc.dll!System.Web.Mvc.DefaultControllerFactory.DefaultControllerActivator.Create(System.Web.Routing.RequestContext requestContext, System.Type controllerType) + 0x2a bytes 
  System.Web.Mvc.dll!System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(System.Web.Routing.RequestContext requestContext, System.Type controllerType) + 0x51 bytes 
  System.Web.Mvc.dll!System.Web.Mvc.DefaultControllerFactory.CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) + 0x4b bytes 
  System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.ProcessRequestInit(System.Web.HttpContextBase httpContext, out System.Web.Mvc.IController controller, out System.Web.Mvc.IControllerFactory factory) + 0xc5 bytes 
  System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.BeginProcessRequest.AnonymousMethod__2() + 0x32 bytes 
  System.Web.Mvc.dll!System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust.AnonymousMethod__a() + 0xe bytes 
  System.Web.Mvc.dll!System.Web.Mvc.SecurityUtil.GetCallInAppTrustThunk.AnonymousMethod__0(System.Action f) + 0x8 bytes 
  System.Web.Mvc.dll!System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(System.Action action) + 0x17 bytes 
  System.Web.Mvc.dll!System.Web.Mvc.SecurityUtil.ProcessInApplicationTrust(System.Func func) + 0x59 bytes 
  System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.BeginProcessRequest(System.Web.HttpContextBase httpContext, System.AsyncCallback callback, object state) + 0x63 bytes 
  System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.BeginProcessRequest(System.Web.HttpContext httpContext, System.AsyncCallback callback, object state) + 0x33 bytes 
  System.Web.Mvc.dll!System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext context, System.AsyncCallback cb, object extraData) + 0x11 bytes 
  System.Web.dll!System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() + 0x227 bytes 
  System.Web.dll!System.Web.HttpApplication.ExecuteStep(System.Web.HttpApplication.IExecutionStep step, ref bool completedSynchronously) + 0x9c bytes 
  System.Web.dll!System.Web.HttpApplication.PipelineStepManager.ResumeSteps(System.Exception error) + 0x342 bytes 
  System.Web.dll!System.Web.HttpApplication.BeginProcessRequestNotification(System.Web.HttpContext context, System.AsyncCallback cb) + 0x60 bytes 
  System.Web.dll!System.Web.HttpRuntime.ProcessRequestNotificationPrivate(System.Web.Hosting.IIS7WorkerRequest wr, System.Web.HttpContext context) + 0xbb bytes 
  System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) + 0x1f3 bytes 
  System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) + 0x1f bytes 
  [Native to Managed Transition] 
  [Managed to Native Transition] 
  System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotificationHelper(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) + 0x350 bytes 
  System.Web.dll!System.Web.Hosting.PipelineRuntime.ProcessRequestNotification(System.IntPtr rootedObjectsPointer, System.IntPtr nativeRequestContext, System.IntPtr moduleData, int flags) + 0x1f bytes 
  [Appdomain Transition]

In this stack you can see all the crazy stuff that your incoming request triggers. This is a lot of code to actually go through, even with a decompiler at hand, so we'll only be roughly speaking about what happens. Below are the most interesting steps. remember, the top of the stack is the last thing to happen, so the items in this list appear in the opposite order to their presentation above.

1) System.Web.Mvc.dll!System.Web.Mvc.DefaultControllerFactory.CreateController (System.Web.Routing.RequestContext requestContext, string controllerName)

Upon the incoming request (to /Home/Index), MVC's DefaultControllerFactory is requested to supply the appropriate controller (HomeController in this case). Note that the requestContext, out of which the controllerName was obtained, are the parameters. The controllerName ("Home") is parsed by MVC from the request URL (more here - old but still quite accurate). The next steps would link this name to the respective controller's classname (really MvcApplication2.Controllers.HomeController, since my dummy project is named MvcApplication2), and instantiate the class.
2) System.Web.Mvc.dll!System.Web.Mvc.DependencyResolver.DefaultDependencyResolver .GetService(System.Type serviceType)

After the controller's Type is determined from its string name (details left to the reader), the CreateController method tried to create it. And who's job is it? Finally, the DependencyResolver! And not any DependencyResolver, but the DefaultDependencyResolver. Decompilation reveals that the DefaultDependencyResolver is an inner class of the DependencyResolver.

How does the DefaultDependencyResolver does this? Namely, how does the GetService implementation looks like? Let's decompile and see:



Looks like the DefaultDependencyResolver just calls Activator.CreateInstance(Type type) . So what does Activator.CreateInstance do? (pssst! It calls Activator.CreateInstance(Type type, bool nonPublic) with the same type and false for a boolean. Below is this second method's code:)
Voila!

Now we've got it! The DefaultDependencyResolver will invoke methods that will finally CreateInstanceDefaultCtor - that is, can only use parameterless constructors!!!

Let's go through the last 2 steps, just to make sure this is really happenning:

3) mscorlib.dll!System.RuntimeType.CreateInstanceSlow(...parameters...)
The depdendency resolver makes the CLR core (we're in mscorlib.dll now...) create the instance, with all of its weird parameters...

4) MvcApplication2.dll!MvcApplication2.Controllers.HomeController
      .HomeController() Line 15 C#
...and finally, the constructor of the right type (our HomeController) is invoked, and this is where our brakepoint was placed (line 15 in the HomeController.cs file).

Dependency Injection in MVC

The conclusion of this journey within the assemblies shows that dependency injection is built into MVC. By default, the actual construction of controllers is separated from the code the requires these controllers. The responsibility for obtaining the controller instance is delegated to the DefaultDependencyResolver. This is exactly what the DI pattern aims at. Too bad the DefaultDependencyResolver is so weak...

Show it to me again, but in the simplest possible way!

Here goes. This is the sole class in new, empty C# project:

So what happens in this single line of code?

The DependencyResolver class' Current public property is invoked, and once the current DependencyResolver is obtained, its method GetService<T> is called (with Foo being T). This is really the DefaultDepepdencyResolver we've seen earlier.

But what if Foo wouldn't have a default constructor? In that case, the CreateInstanceDefaultCtor method we've seen before would throw an exception: System.MissingMethodException: No parameterless constructor defined for this object. Add a new constructor to the controller that has some parameters (even an int would suffice) and you'll see it happening in an MVC application as well.

Conclusion: So why using the DefaultDependencyResolver?

If you're writing ASP.NET MVC applications, the (Default)DependencyResolver is a given fact. Sure, you can replace the default controller factory, but a) Why would you do that? and b) there's a chance even your custom controller factory will use a DI of some sort.

DefaultDependencyResolver implements the IDependencyResolver interface, which is also a part of the MVC assembly. Here's what it can do:

And since the DependencyResolver (not an implementor of the interface, by the way!) has a public SetResolver method, what we see here really is an extension point.

See, the MVC people know their DependencyResolver isn't what you need! It just serves as a placeholder for something more exciting and capable. Something like MEF, Castle Windsor, Ninject, etc. To start understanding MEF, one should first understand how life without MEF would look like. What MEF has come to replace.



Download JustDecompile from Telerik: http://www.telerik.com/products/decompiler.aspx

For the code snippet appearing above I've used Alex Gorbatchev's Syntax Highlighter, which I incorporated into Blogger by following Tahir Hassan's instructions. I won't be moving to gists since I don't have anything important enough to be forked etc.