Castle Windsor Dependency Resolver for MVC 3
Asked Answered
S

3

34

Since the IoC/DI implementation in MVC 3 is most likely in its final form in the RC, I'm looking for an updated implementation of the DependencyResolver, IControllerActivator and IViewPageActivator using Caste Windsor. Are there any examples out there that have been updated for MVC 3 RC?

EDIT #1 Implementing a Windsor dependency resolver is indeed trivial, but there's still something missing. Contrary to Jeff Putz's Ninject example (below), it appears that it's not as simple as that with Windsor. After setting the dependency resolver like so,

DependencyResolver.SetResolver(new WindsorDependencyResolver(container)); 

Windsor throws ComponentNotFoundException. I need to provide implementations for IControllerFactory and IControllerActivator. Since the DefaultControllerFactory is DependencyResolver aware, this can be solved as follows:

Component.For<IControllerFactory >().ImplementedBy<DefaultControllerFactory>()
Component.For<IControllerActivator >().ImplementedBy<WindsorControllerActivator>(),

WindsorControllerActivator is trivial as well. However, this leads to another ComponentNotFoundException for IViewPageActivator.

This leads me to believe that I'm missing something. There is no way that this should be more complicated than an implementing a controller factory and calling ControllerBuilder.Current.SetControllerFactory MVC 2.0-style.

EDIT #2 I missed the subtle but important detail that the Dependency resolver needs to return null when a service cannot be found. The implementation is as follows:

public class WindsorDependencyResolver : IDependencyResolver
{
    private readonly IWindsorContainer container;

    public WindsorDependencyResolver(IWindsorContainer container)
    {
        this.container = container;
    }

    public object GetService(Type serviceType)
    {
        return container.Kernel.HasComponent(serviceType) ? container.Resolve(serviceType) : null;
    }

  public IEnumerable<object> GetServices(Type serviceType)
    {
        return container.Kernel.HasComponent(serviceType) ? container.ResolveAll(serviceType).Cast<object>() : new object[]{};
    }
}

EDIT #3

Responding to a question in the comments. If you do find that you need your own IControllerActivator, here a simple implementation for Windsor:

public class WindsorControllerActivator : IControllerActivator
{
    private readonly IWindsorContainer container;

    public WindsorControllerActivator(IWindsorContainer container)
    {
        this.container = container;
    }

    public IController Create(RequestContext requestContext, Type controllerType)
    {
        return (IController)container.GetService(controllerType);
    }
}

}

Again, this is NOT necessary to get basic DI working with Windsor and the MVC3 dependency resolver.

EDIT #4 Based on some further research and feedback, it seems that a traditional controller factory implementation is the best approach for Windsor and MVC3. The concern is that the IDependencyResolver interface lacks a release method, which could cause memory leaks with Windsor not disposing its components. This is probably not going to be an issue if all of your dependencies are resolved with the PerWebRequest lifecycle, but it's still better not to take the chance. Here's a basic implementation of a Windsor controller factory for MVC3.

public class WindsorControllerFactory : DefaultControllerFactory
{
    private readonly IWindsorContainer container;

    public WindsorControllerFactory(IWindsorContainer container)
    {
        this.container = container;
    }

    public override void ReleaseController(IController controller)
    {
        container.Kernel.ReleaseComponent(controller);
    }

    public override IController CreateController(RequestContext requestContext, string controllerName)
    {
        var controllerComponentName = controllerName + "Controller";
        return container.Kernel.Resolve<IController>(controllerComponentName);
    }
}

EDIT #5 If you're using MVC areas, the above implementation will not work for you. You will need to register each controller based on its full name, and override GetControllerInstance instead of CreateController:

 protected override IController GetControllerInstance(RequestContext context, Type controllerType)
    {
        if (controllerType != null)
        {
            return (IController)container.Kernel.Resolve(controllerType);
        }
        return null;
    }
Substage answered 10/11, 2010 at 3:2 Comment(7)
MVC 3 is 15 hours old already, somebody write this code stat!Tetrameter
Yes... the null return part is key, which isn't as obvious in my example if you don't know that Ninject has that TryGet method and returns null if it can't find a match. I probably could have been more clear. :)Revolutionary
Any chance we can see more of this code. WindsorControllerActivator? where is that, google zero results.Yepez
It's not required to get this working...but there's nothing to it. WindsorControllerActivator implements IControllerActivator which just has a single method which returns IController.Substage
Ahh thanks, I am a late comer to the church of Ioc but I have succumbed to it now and am catching up.Yepez
This activator is flawed, I tried to use it but if you register this activator with your DI container, the DefaultControllerFactory will use this activator. When the create is called, if the container does not have the controller in it, it will fail giving you the same error message as the author saw. To solve this you can check for null from the container, then add it if its null. if (controller == null) { container.Register(Component .For(controllerType) .LifeStyle.Singleton); controller = (IController)container.GetService(controllerType); }Sergiosergipe
And based on all this I might use NinjectKeynes
R
10

The interface has not changed since the beta release, so all of the implementations for various frameworks should still work. And the truth is, it's not that complicated of an interface... you should be able to roll your own without much hassle. For example, I did this one for Ninject:

public class NinjectDependencyResolver : IDependencyResolver
{
    public NinjectDependencyResolver(IKernel kernel)
    {
        _kernel = kernel;
    }

    private readonly IKernel _kernel;

    public object GetService(Type serviceType)
    {
        return _kernel.TryGet(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _kernel.GetAll(serviceType);
    }
}

Then wire it up in global.asax like this:

    private static IKernel _kernel;
    public IKernel Kernel
    {
        get { return _kernel; }
    }

    public void Application_Start()
    {
        _kernel = new StandardKernel(new CoreInjectionModule());
        DependencyResolver.SetResolver(new NinjectDependencyResolver(Kernel));
        ...
    }

Remember, you get all kinds of goodies for free at that point, including DI for controllers, controller factories, action filters and view base classes.

EDIT: To be clear, I'm not sure what your "activators" are, but you probably don't need them. The IDependencyResolver interface handles the newing-up of controllers and views automagically.

Revolutionary answered 10/11, 2010 at 4:25 Comment(1)
Hiya, why are you storing the kernal in a public, static property of the application class? I'm new to Ninject, but don't I only need this: var resolver = new StandardKernel(new MyNinjectModule()); DependencyResolver.SetResolver(new NinjectDependencyResolver(resolver)); Thanks!Ruck
H
24

The MVC3 IDependencyResolver interface has a big problem: no release method. This means that there is a potential memory leak if you are going to use it with Windsor. See my blog post about it here:

http://mikehadlow.blogspot.com/2011/02/mvc-30-idependencyresolver-interface-is.html

Hipbone answered 3/2, 2011 at 17:6 Comment(0)
R
10

The interface has not changed since the beta release, so all of the implementations for various frameworks should still work. And the truth is, it's not that complicated of an interface... you should be able to roll your own without much hassle. For example, I did this one for Ninject:

public class NinjectDependencyResolver : IDependencyResolver
{
    public NinjectDependencyResolver(IKernel kernel)
    {
        _kernel = kernel;
    }

    private readonly IKernel _kernel;

    public object GetService(Type serviceType)
    {
        return _kernel.TryGet(serviceType);
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        return _kernel.GetAll(serviceType);
    }
}

Then wire it up in global.asax like this:

    private static IKernel _kernel;
    public IKernel Kernel
    {
        get { return _kernel; }
    }

    public void Application_Start()
    {
        _kernel = new StandardKernel(new CoreInjectionModule());
        DependencyResolver.SetResolver(new NinjectDependencyResolver(Kernel));
        ...
    }

Remember, you get all kinds of goodies for free at that point, including DI for controllers, controller factories, action filters and view base classes.

EDIT: To be clear, I'm not sure what your "activators" are, but you probably don't need them. The IDependencyResolver interface handles the newing-up of controllers and views automagically.

Revolutionary answered 10/11, 2010 at 4:25 Comment(1)
Hiya, why are you storing the kernal in a public, static property of the application class? I'm new to Ninject, but don't I only need this: var resolver = new StandardKernel(new MyNinjectModule()); DependencyResolver.SetResolver(new NinjectDependencyResolver(resolver)); Thanks!Ruck
S
2

MVCContrib is currently the authoritative source for IoC-MVC integration. Currently, the MVC3 branch only includes controller factory and IDependencyResolver implementations (and a couple other things). I recommend forking the repository and implementing the missing extension points (shouldn't be too hard), then send the team a pull request.

Station answered 10/11, 2010 at 4:16 Comment(1)
No longer! For anyone who reads this now: mvccontrib.codeplex.com/workitem/7122Imperfect

© 2022 - 2024 — McMap. All rights reserved.