IoC: Castle Windsor and WebAPI
Asked Answered
C

4

22

I have an MVC4 site using Castle Windsor that I want to add some WebAPI calls to, so I start digging around a little bit on the interwebs.

Now I don't know the ins and outs of IoC; I followed a tutorial for how to set up Castle Windsor on my project, injecting the IUnitOfWorkFactory and the IApplicationService as public properties in a base controller, and a few other interfaces as needed in the controller constructors. It works swimmingly, so I've never had to do more with it.

Everywhere that I'm reading up on WebAPI, I'm told DI will not work so well using Castle Windsor, talking about problems with the IDependencyResolver and IDependencyScope. There are several workarounds and implementations of how to get around this problem, but what is not clear to me is what exactly the problem is. Code snippets are included, but the assumption is you know what class they belong to, and how they are invoked, which unfortunately I do not. Additionally, all the examples I've seen online refer to an exclusive WebAPI project, and not an MVC4 project with a couple ApiControllers judiciously tossed in. I don't know how, or if, this affects anything.

Why won't what I have working with my standard controllers not work with an API controller? What kind of code acrobatics need to do to get WebAPI calls and standard web calls to work in the same application?

Catamount answered 22/4, 2013 at 18:55 Comment(3)
Any references? I have a mvc4 app with web and webapi controllers and ioc works like a charm for both. Could be something specific with Castle Windsor, though, as we are using another framework.Pilaf
@WiktorZychla I'm basically looking at a google list from querying on "webapi castle windsor"... many different things, many different ways, many complaints about the way IDependencyResolver is built. Out of curiosity, which IoC container are you using?Catamount
Unity. And I really don't remember any serious issues. There is a unity controller factory set up to resolve controllers for browser requests and the unity dependency resolver to resolve web api controllers. As long as routes do not overlap, this just works.Pilaf
B
12

In order to use Windsor with webapi follow http://blog.ploeh.dk/2012/10/03/DependencyInjectioninASP.NETWebAPIwithCastleWindsor/ (BEH: fixed link)

Read also Castle Windsor/DelegatingHandler/IPrincipal Dependency Injection (DI)/Inversion of Control (IoC) in ASP.NET Web API

You can use webapi and MVC controllers in the same app (I prefer to keep it separate) but routing and controllers factory are different and you have to set up two different configuration and handle routing overlaps... For MVC & Windsor you can find a great

Basinger answered 23/4, 2013 at 3:50 Comment(6)
Do I need to do anything special to wire this up?Catamount
You mean webapi / MVC side by side? Not at all.Basinger
first link in this answer is invalidActinomycosis
BEH? For what does that stand?Uretic
+1 I had to read Seemann's article a couple of times, but it was the right answer. Thanks!Converge
Talk about a cliff hangerArwood
I
14

Existing Castle Windsor MVC configuration

Assuming you have MVC and Castle Windsor setup similarly to the Castle Windsor MVC tutorial, adding IoC to get Web API controllers to utilize dependency injection is very simple with Mark Seemann's post (note that he explains why not to use IDependencyResolver).

From the Castle Windsor tutorial you should have something like this in Global.asax.cs.

    private static IWindsorContainer container;

    protected void Application_Start()
    {
        //... MVC / Web API routing etc.
        BootStrapper bs = new BootStrapper();
        container = bs.ConfigureCastleWindsorMVC();
    }

BootStrapper.ConfigureCastleWindsorMVC() snip

        IWindsorContainer container = new WindsorContainer()
            .Install(
                new LoggerInstaller()
                //...,
                , new ControllersInstaller()
            );

        var controllerFactory = new WindsorControllerFactory(container.Kernel);
        ControllerBuilder.Current.SetControllerFactory(controllerFactory);
        return container;

Required changes

From Mark Seemann's post you need to get into Web API's entry point (Composition Root) through the IHttpControllerActivator. Here's his adapter implementation which you need.

public class WindsorCompositionRoot : IHttpControllerActivator
{
    private readonly IWindsorContainer container;

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

    public IHttpController Create(HttpRequestMessage request,
        HttpControllerDescriptor controllerDescriptor, Type controllerType)
    {
        var controller =
            (IHttpController)this.container.Resolve(controllerType);

        request.RegisterForDispose(
            new Release(() => this.container.Release(controller)));

        return controller;
    }

    private class Release : IDisposable
    {
        private readonly Action release;

        public Release(Action release) { this.release = release; }

        public void Dispose()
        {
            this.release();
        }
    }
}

With the IHttpControllerActivator adapter and the MVC Castle Windsor container implementation, you just need to configure it in the Global.asax.cs (or in BootStrapper if you used that). It has to be after the MVC initialization since the MVC initialization has all of the installers.

    private static IWindsorContainer container;

    protected void Application_Start()
    {
        // MVC / Web API routing etc.
        BootStrapper bs = new BootStrapper();
        container = bs.ConfigureCastleWindsorMVC();
        // Web API Castle Windsor ++ ADD THIS ++
        GlobalConfiguration.Configuration.Services.Replace(
            typeof(IHttpControllerActivator),
            new WindsorCompositionRoot(container));
    }

Final Result:

The Web API controllers can use your injected dependencies the same as your MVC controllers.

public class TestController : ApiController
{
    private readonly ITestService TestService;

    public TestController(ITestService testService)
    {
        this.TestService = testService;
    }

    // GET api/<controller>
    public IEnumerable<string> Get()
    {
        return TestService.GetSomething();
        //return new string[] { "value1", "value2" };
    }
}
Ia answered 27/9, 2014 at 14:50 Comment(0)
B
12

In order to use Windsor with webapi follow http://blog.ploeh.dk/2012/10/03/DependencyInjectioninASP.NETWebAPIwithCastleWindsor/ (BEH: fixed link)

Read also Castle Windsor/DelegatingHandler/IPrincipal Dependency Injection (DI)/Inversion of Control (IoC) in ASP.NET Web API

You can use webapi and MVC controllers in the same app (I prefer to keep it separate) but routing and controllers factory are different and you have to set up two different configuration and handle routing overlaps... For MVC & Windsor you can find a great

Basinger answered 23/4, 2013 at 3:50 Comment(6)
Do I need to do anything special to wire this up?Catamount
You mean webapi / MVC side by side? Not at all.Basinger
first link in this answer is invalidActinomycosis
BEH? For what does that stand?Uretic
+1 I had to read Seemann's article a couple of times, but it was the right answer. Thanks!Converge
Talk about a cliff hangerArwood
G
6

The following sample project gave me the answer I was looking for. It uses Castle Windsor for dependency injection. I was able to use MVC Controllers alongside Web API controllers on the same Application.

mirajavora / WebAPISample


Here's the post detailing it:

Getting Started with ASP.NET Web API

Gymnastic answered 26/10, 2013 at 23:41 Comment(0)
C
1

First as Iko stated you need to create a class that implements IHttpControllerActivator.

Then in ContainerBootstrapper in Bootstrap() add the following Replace the default with the one implemented:

 //This will help in creating Web api with Dependency injection
 GlobalConfiguration.Configuration.Services.Replace(
 typeof(IHttpControllerActivator),
 new WindsorCompositionRoot(container));

Lastly which is not mentioned here and it didn't work for me without it that you should add the following in you implemented IWindsorInstaller service inside install() :

container.Register(
    Classes.FromThisAssembly()
    .BasedOn<ApiController>()
    .LifestyleTransient()    
);
Charily answered 12/8, 2018 at 8:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.