Autofac and ASP.NET Web API System.Web.Http.Services.IDependencyResolver Integration

In this post, you can make Autofac work with ASP.NET Web API System.Web.Http.Services.IDependencyResolver. Solution to the 'controller has no parameterless public constructor' error.
2012-02-27 09:43
Tugberk Ugurlu


I have been using Ninject in all of my ASP.NET MVC applications till Chris Marisic (@dotnetchris) poked me on twitter about how slow Ninject is. After a little bit of research, I changed it to Autofac which works pretty perfectly.

ASP.NET Web API has nearly the same Dependency Injection support as ASP.NET MVC. As we do not have a built in support for ASP.NET Web API in Autofac (yet), I created a simple one. The implementation is not as straight forward as Ninject and you probably saw the below error if you tried to make it work:

System.InvalidOperationException:

An error occurred when trying to create a controller of type 'TourismDictionary.APIs.Controllers.WordsController'. Make sure that the controller has a parameterless public constructor.

Let’s see how it works:

First I created a class which implements System.Web.Http.Services.IDependencyResolver interface.

internal class AutofacWebAPIDependencyResolver : 
    System.Web.Http.Services.IDependencyResolver {

    private readonly IContainer _container;

    public AutofacWebAPIDependencyResolver(IContainer container) {

        _container = container;
    }

    public object GetService(Type serviceType) {

        return 
            _container.IsRegistered(serviceType) ? 
            _container.Resolve(serviceType) : null;
    }

    public IEnumerable<object> GetServices(Type serviceType) {

        Type enumerableServiceType = 
            typeof(IEnumerable<>).MakeGenericType(serviceType);
            
        object instance = 
            _container.Resolve(enumerableServiceType);
            
        return ((IEnumerable)instance).Cast<object>();
    }
}

And I have another class which holds my registrations and sets the ServiceResolver with GlobalConfiguration.Configuration.ServiceResolver.SetResolver method which is equivalent to DependencyResolver.SetResolver method:

internal class AutofacWebAPI {

    public static void Initialize() {
        var builder = new ContainerBuilder();
        GlobalConfiguration.Configuration.ServiceResolver.SetResolver(
            new AutofacWebAPIDependencyResolver(RegisterServices(builder))
        );
    }

    private static IContainer RegisterServices(ContainerBuilder builder) {

        builder.RegisterAssemblyTypes(
            typeof(MvcApplication).Assembly
        ).PropertiesAutowired();

        //deal with your dependencies here
        builder.RegisterType<WordRepository>().As<IWordRepository>();
        builder.RegisterType<MeaningRepository>().As<IMeaningRepository>();

        return
            builder.Build();
    }
}

Then, initialize it at Application_Start:

public class MvcApplication : System.Web.HttpApplication {

    private void Configure(HttpConfiguration httpConfiguration) {

        httpConfiguration.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }

    protected void Application_Start() {

        Configure(GlobalConfiguration.Configuration);
        AutofacWebAPI.Initialize();
    }

}

There are different ways of doing it but I like this approach.



Comments

Tugberk
by Tugberk on Friday, Mar 09 2012 09:51:38 +02:00

@Alex

Nice! Thanks for sharing that.

Leo Gorodinski
by Leo Gorodinski on Saturday, Mar 10 2012 02:05:42 +02:00

I think you're fogetting to create a new lifetime scope per request and use that scope instead of the container to resolve dependencies. The API controller instances cannot be reused.

New Comment