Dependency injection not working with Owin self-hosted Web Api 2 and Autofac
Asked Answered
C

1

26

I'm finding my feet with Web Api 2, Owin and Autofac and need some guidance, please.

Overview
I have an Owin self-hosted Web Api that uses Autofac for IoC and dependency injection. The project is a console app acting like a service, meaning it can be stopped and started. I have an Authentication controller with two constructors: one parameter-less and the other injects a repository.

Problem
When I run the service and call the api, my parameter-less constructor is called and my repository never gets injected (_repository = null).

Research
I've done a fair bit of research and found some helpful projects on Github, which I replicated to the tee but I'm missing a big part of the puzzle. This was helpful but didn't solve my problem. I read this question on Stack Overflow and Dane Sparza had a nice demo project but I couldn't find a clear solution. The problem is not the self-hosting but the dependency injection.

My code (thinned out for explanation)

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration config = new HttpConfiguration();

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

        var json = config.Formatters.JsonFormatter;
        json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
        config.Formatters.Remove(config.Formatters.XmlFormatter);

        var connectioninfo = ConnectionInfo.FromAppConfig("mongodb");

        var builder = new ContainerBuilder();                                    // Create the container builder.
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());         // Register the Web API controllers.
        builder.Register(c => new Logger()).As<ILogger>().InstancePerRequest();  // Register a logger service to be used by the controller and middleware.
        builder.RegisterType<AuthenticationRepository>().As<IAuthenticationRepository>().WithParameter(new NamedParameter("connectionInfo", connectioninfo)).InstancePerRequest();

        var container = builder.Build();

        var resolver = new AutofacWebApiDependencyResolver(container);           // Create an assign a dependency resolver for Web API to use.
        GlobalConfiguration.Configuration.DependencyResolver = resolver;         // Configure Web API with the dependency resolver

        app.UseCors(CorsOptions.AllowAll);  
        app.UseWebApi(config);
        app.UseAutofacWebApi(config);  // Make sure the Autofac lifetime scope is passed to Web API.
    }

Program.cs

 static void Main(string[] args)
    {           
        var service = new ApiService(typeof(Program), args);

        var baseAddress = "http://localhost:9000/";
        IDisposable _server = null;

        service.Run(
           delegate()
           {
               _server = WebApp.Start<Startup>(url: baseAddress);
           },
           delegate()
           {
               if (_server != null)
               {
                   _server.Dispose();
               }
           }
       );
    }

ApiController

public class AuthenticationController : ApiController
{
    private IAuthenticationRepository _repository;

    public AuthenticationController() { }

    public AuthenticationController(IAuthenticationRepository repository)
    {
        _repository = repository;
    }

    [AllowAnonymous]
    public IHttpActionResult Authenticate(string name, string password)
    {
        if (_repository == null)
            return BadRequest("User repository is null.");

        var valid = _repository.AuthenticateUser(name, password);
        return Ok(valid);
    }
}
Coprology answered 7/8, 2014 at 8:32 Comment(2)
What is the point of the empty constructor? Since you require a repository, just have the AuthenticationController(IAuthenticationRepository repository) constructor. That way you don't need the if (_repository == null) check in every methodCle
See my reply on Marcel N's answer. The way I was setting the Dependency Resolver required me to have an empty constructor.Coprology
K
39

You should be using the HttpConfiguration with which you're bootstrapping OWIN everywhere. So, this:

GlobalConfiguration.Configuration.DependencyResolver = resolver;

Should become:

config.DependencyResolver = resolver;

Other than that, everything looks good. Api controllers are registered, although you're not giving them a scope. Not sure if in Autofac scoping defaults to per-request for controllers or if it has the notion of per-request scoping at all (I know that LightInject has it).

Looking around, I think you followed the example on the Google Code repo for Autofac, which indeed uses GlobalConfiguration. Instead, if you look at the GitHub example, it is a bit different. Try to make the changes according to this. Including this:

// This should be the first middleware added to the IAppBuilder.
app.UseAutofacMiddleware(container);

2016 update

What I said above still applies, but something extra from Autofac's docs (thanks Brad):

A common error in OWIN integration is use of the GlobalConfiguration.Configuration. In OWIN you create the configuration from scratch. You should not reference GlobalConfiguration.Configuration anywhere when using the OWIN integration.

Kattiekatuscha answered 7/8, 2014 at 8:37 Comment(2)
Yes, you're spot-on! Your suggestions along with @Trevor Pilley's answer solved the problem and it's properly injecting now. The GitHub project in your link is great, thanks for the help.Coprology
For any new-comers, here's Autofac's docs regarding Owin/WebApi: autofac.readthedocs.org/en/latest/integration/…Follicle

© 2022 - 2024 — McMap. All rights reserved.