Web Api (beta) and Castle.Windsor…without IDependencyResolver ?
I've been reading through several blog post about how people configure DI support in the new ASP.NET Web Api. For example you can read about it here :
- Official "Using the Web API Dependency Resolver"
- "Autofac ASP.NET Web API (Beta) Integration"
- "Web Api and Ninject"
What stands out is that every implementation relays on System.Web.Http.Services.IDependencyResolver
interface which is similar to the one introduced in the ASP.NET MVC 3 and which lives in the System.Web.Mvc
namespace to avoid confusion. Let's look at its implementation details :
1: 2: 3: 4: 5: |
|
Since it was introduced in MVC 3 I've never used it because of its lack of "Release" method and the fact that there is no way cleaning-up resolved services from you DI container. If it's not a problem for all DI containers, it is certainly a problem for Castle.Windsor which tracks resolved components. Mike Hadlow already spotted it was a problem and wrote a blog post "The MVC 3.0 IDependencyResolver interface is broken. Don't use it with Windsor." I think that this problem still remains with a WebApi IDependencyResolver
interface and that’s why I decided to not use it. But will I manage to do it ?
Self Hosting
My current WebApi project is using self hosting feature and for the moment I won't be interested in IIS hosting. After all, there should not be significant differences between two hosting options on adding IoC container support. We will check it after.
Given that in ASP.NET MVC 3 we weren't compelled to implement IDependencyResolver
at all I started to figure out what other hooks WebApi offers in order to set up my Composition Root. After few minutes of inspecting WebApi's API, I found IHttpControllerFactory
in the System.Web.Http.Dispatcher
namespace which seems to me like a viable seam for connecting my own implementation. This interface is quite similar to the one from the System.Web.Mvc
namespace and is all what I need.
1: 2: 3: 4: 5: 6: |
|
As you noticed, there is "Create" and "Release" controller methods which allows me to use it with Castle.Windsor. Let's look at it :
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: |
|
Straightforward implementation that doesn't need any comments. Next step is to register it in the WebApi framework.
First Fail : Registration
I really should start from there... Looking into the WebApi's API I can't really find where to register my own implementation into the framework. It seems that there is nothing similar to ControllerBuilder.Current.SetControllerFactory
. My current startup code is like this :
1: 2: 3: 4: 5: 6: |
|
Once again, standard initialization. But there is nothing on the HttpSelfHostConfiguration
class that allows defining your own IHttpControllerFactory
implementation. The only thing I can see is HttpSelfHostConfiguration.ServiceResolver.SetResolver
methods with it's overrides. It seems that there is no way to avoid implementing IDependencyResolver
interface. So let's do it.
Rolling out my own Dependency Resolver
As my unique solution is to implement my own IDependencyResolver
so let's do it :
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: |
|
Then we have to register it as follows :
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: |
|
This is not sufficient because of the problem described at the beginning of this post (the lack of the "Release" method and thus being unable to release components resolved with Castle.Windsor). The next step is to figure out how to register our implementation of IHttpControllerFactory
. The good news is that that framework WebApi calls every time to our implementation of IDependencyResolver
in the hope that it could find a service for every seam that is try to resolve. One of that seams is IHttpControllerFactory
so it easy to register our own implementation with Castle.Windsor :
1: 2: 3: 4: 5: 6: 7: 8: 9: |
|
Note, that the default liftetime when unspecified is Singleton.
We're ready to test.
Second Fail : Testing in Fiddler
To my surprise, when calling my service I end up with the following error (sorry for the French) :
{"ExceptionType":"System.NullReferenceException","Message":"La référence d’objet n’est pas définie à une instance d’un objet.","StackTrace":" à System.Web.Http.ApiController.ExecuteAsync(HttpControllerContext controllerContext, CancellationToken cancellationToken)u000du000a à System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsyncInternal(HttpRequestMessage request, CancellationToken cancellationToken)u000du000a à System.Web.Http.Dispatcher.HttpControllerDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)"}
For me it brakes the POLA. After wandering on Internet I didn't find anything helpful. I decompiled some WebApi code sources, debugged once again and found that the instance of HttpControllerContext
passed to my controller factory was a bit strange. In fact ControllerDescriptor
and Controller properties was not assigned. By trial and error I ended up modifying my implementation like this :
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: |
|
Now everything works as expected. It's a bit weird as these properties seem to be optional from the point of view of the API, or at least they should be provided with the instance of HttpControllerContext
.
In Closing
As you see, there is no way to get rid of IDependencyResolver
in the WebApi compared to MVC. It's a pity because it puts another burden on the developer to implement an interface that doesn't act as a composition root. In fact our composition root is resolved from the implementation of IDependencyResolver
so it's a bit confusing. It's understandable from the point of view of ASP.NET team which wanted to make IDependencyResolver
a central piece of IoC support in the WebApi framework but for me it presents some design flaws as the API is not clear enough which seams should be used and how exactly implement them.
In the next post I'll do the test implementing it in IIS hosting environment.