C# – Dependency Injection in WebAPI with Castle Windsor

asp.net-web-apiccastle-windsorwcf-web-api

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!

Best Answer

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