Dependency Injection in WebAPI with Castle Windsor
Asked Answered
H

3

21

I want to implement Dependency Injection in WebApi application using Castle Windsor. I have following sample code -

Interface -

public interface IWatch
{
    {
        DateTime GetTime();
    }
}

Following Watch class implements IWatch Interface -

public class Watch:IWatch
{
        public DateTime GetTime()
        {
            return DateTime.Now;
        }
}

WebApi Controller - WatchController as below -

public class WatchController : ApiController
{
        private readonly IWatch _watch;

        public WatchController()
        {
            _watch = new Watch();
        }

        //http://localhost:48036/api/Watch
        public string Get()
        {
            var message = string.Format("The current time on the server is: {0}", _watch.GetTime());
            return message;
        }
}

Currently I am initiating IWatch object with Watch in WatchController constructor. I want to remove dependency of initializing IWatch inside constructor using Windsor Castle dependency injection principle.

Can anybody provide me the steps to implement dependency injection in this case of WebApi? Thanks in advance!

Harve answered 11/11, 2013 at 11:26 Comment(3)
nikosbaxevanis.com/blog/2012/06/04/…, haacked.com/archive/2012/03/11/…, gist.github.com/moodmosaic/2044349, github.com/WebApiContrib/WebApiContrib.IoC.CastleWindsor, did you try anything?Eloyelreath
Yes, I have already tried with this links but not found relevant solution.Harve
Then please explain what specific part you're having trouble with.Eloyelreath
H
71

CodeCaster, Noctis and Cristiano thank you for all your help and guidance.. I just got the solution for my above query -

The first step is to use nuget to install the Windsor.Castle packages in the WebApi solution.

enter image description here

Consider the following code snippet -

Interface IWatch.cs

public interface IWatch
{
     DateTime GetTime();
}

Class Watch.cs

public class Watch:IWatch
{
    public DateTime GetTime()
    {
        return DateTime.Now;
    }
}

The ApiController WatchController.cs is defined as follows: -

public class WatchController : ApiController
{
     private readonly IWatch _watch;

     public WatchController(IWatch watch)
     {
         _watch = watch;
     }

     public string Get()
     {
         var message = string.Format("The current time on the server is: {0}", _watch.GetTime());
         return message;
     }
}

In the controller we have injected the dependency through IWatch object in the WatchController constructor. I have used IDependencyResolver and IDependencyScope to achieve dependency injection in web api. The IDependencyResolver interface is used to resolve everything outside a request scope.

WindsorDependencyResolver.cs

internal sealed class WindsorDependencyResolver : IDependencyResolver
{
    private readonly IWindsorContainer _container;

    public WindsorDependencyResolver(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }

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

    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t).Cast<object>().ToArray();
    }

    public IDependencyScope BeginScope()
    {
        return new WindsorDependencyScope(_container);
    }

    public void Dispose()
    {

    }
}

WindsorDependencyScope.cs

internal sealed class WindsorDependencyScope : IDependencyScope
{
    private readonly IWindsorContainer _container;
    private readonly IDisposable _scope;

    public WindsorDependencyScope(IWindsorContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        _container = container;
        _scope = container.BeginScope();
    }

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

    public IEnumerable<object> GetServices(Type t)
    {
        return _container.ResolveAll(t).Cast<object>().ToArray();
    }

    public void Dispose()
    {
        _scope.Dispose();
    }
}

WatchInstaller.cs

Installers are simply types that implement the IWindsorInstaller interface. The interface has a single method called Install. The method gets an instance of the container, which it can then register components with using fluent registration API:

public class WatchInstaller : IWindsorInstaller
{
      public void Install(IWindsorContainer container, IConfigurationStore store)
      {
      //Need to Register controllers explicitly in your container
      //Failing to do so Will receive Exception:

      //> An error occurred when trying to create //a controller of type
      //> 'xxxxController'. Make sure that the controller has a parameterless
      //> public constructor.

      //Reason::Basically, what happened is that you didn't register your controllers explicitly in your container. 
      //Windsor tries to resolve unregistered concrete types for you, but because it can't resolve it (caused by an error in your configuration), it return null.
      //It is forced to return null, because Web API forces it to do so due to the IDependencyResolver contract. 
      //Since Windsor returns null, Web API will try to create the controller itself, but since it doesn't have a default constructor it will throw the "Make sure that the controller has a parameterless public constructor" exception.
      //This exception message is misleading and doesn't explain the real cause.

      container.Register(Classes.FromThisAssembly()
                            .BasedOn<IHttpController>()
                            .LifestylePerWebRequest());***
          container.Register(
              Component.For<IWatch>().ImplementedBy<Watch>()
          );
      }
}

Finally, we need to replace the default dependency resolver with the Windsor implementation in Global.asax.cs (Application_Start method) and install our dependencies:

    private static IWindsorContainer _container;
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        ConfigureWindsor(GlobalConfiguration.Configuration);
    }

    public static void ConfigureWindsor(HttpConfiguration configuration)
    {
        _container = new WindsorContainer();
        _container.Install(FromAssembly.This());
        _container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
        var dependencyResolver = new WindsorDependencyResolver(_container);
        configuration.DependencyResolver = dependencyResolver;
    }    
Harve answered 19/11, 2013 at 12:38 Comment(7)
I find it pretty amazing that this doesn't have more than a handful upvotes. For all the work you put in, you deserve dozens!Psilocybin
What if you have different implementations of IWatch?Bentonbentonite
Please specify your scenario in detailHarve
why you made the container static?Emu
@SumitDeshpande , I have followed your answer.However, I am getting "Object reference not set to an instance of an object" error message.Do you any idea on this? If you want I will keep my code which I have written.Jacklight
Whats this for: "container.Kernel.Resolver.AddSubResolver(new CollectionResolver(container.Kernel, true));" in my case it works without this, is not the same example as this one though.Tansy
Using this method can lead to some pretty nasty memory leaks. Im not quite sure of the internals, but it seems that castle keeps a reference to all httprequestmessages, which results in memory not being released. Using the approach from Mark Seemann seems to work fine (see other suggetsted solution). Using GlobalConfiguration.Configuration.Services.Replace( typeof(IHttpControllerActivator), new WindsorCompositionRoot(this.container)); might also work, but have not triedBezoar
C
6

Read Mark Seemann post about windsor plumbing for webapi.

Cinerarium answered 12/11, 2013 at 8:39 Comment(0)
D
1

I didn't work directly with Castle Windsor, but I believe the logic should be similar:

Your WatchController ctor should look like this:

public WatchController(IWatch watch) 
{
    _watch = watch;
}

And this is where you inject the dependency.

You should have the equivalent to a Locator in which you register your WatchController class, and tell it which watch it should receive depending on whatever you want ... design/runtime , day of the week, random number ... whatever works or whatever you need...

The following code is from MVVM-Light, but should clarify the above paragraph:

static ViewModelLocator()
{
    ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

    // This will run in design mode, so all your VS design data will come from here
    if (ViewModelBase.IsInDesignModeStatic)
    {
        SimpleIoc.Default.Register<IDataService, Design.DesignDataService>();
    }
    // This will run REAL stuff, in runtime
    else
    {
        SimpleIoc.Default.Register<IDataService, DataService>();
    }

    // You register your classes, so the framework can do the injection for you
    SimpleIoc.Default.Register<MainViewModel>();
    ...
}
Dimmick answered 11/11, 2013 at 11:40 Comment(2)
I am new to MVVM it would be helpful if you could elaborate your response in detail. Thanks!Harve
Like CodeCaster said, what did you try? be more specific. Everybody has better things to do than writing other people's code. If you want a quick reading about DI, look here: Blog by James Shore. If you want the heavy guns (bit longer though) try this article by Martin Fowler. If you want to be more specific, update your questionDimmick

© 2022 - 2024 — McMap. All rights reserved.