Dependency Injection in WebAPI with Castle Windsor

17,031

Solution 1

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;
    }    

Solution 2

Read Mark Seemann post about windsor plumbing for webapi.

Solution 3

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>();
    ...
}
Share:
17,031

Related videos on Youtube

Sumit Deshpande
Author by

Sumit Deshpande

Updated on March 14, 2020

Comments

  • Sumit Deshpande
    Sumit Deshpande about 4 years

    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!

  • Sumit Deshpande
    Sumit Deshpande over 10 years
    I am new to MVVM it would be helpful if you could elaborate your response in detail. Thanks!
  • Noctis
    Noctis over 10 years
    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 question
  • Tomas Aschan
    Tomas Aschan over 9 years
    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!
  • Admin
    Admin about 8 years
    What if you have different implementations of IWatch?
  • Sumit Deshpande
    Sumit Deshpande about 8 years
    Please specify your scenario in detail
  • Mariusz Jamro
    Mariusz Jamro over 7 years
    why you made the container static?
  • Balanjaneyulu K
    Balanjaneyulu K over 6 years
    @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.
  • Guillermo Ruffino
    Guillermo Ruffino about 6 years
    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.
  • David Kristensen
    David Kristensen about 5 years
    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 tried