依赖注入不适用于 Owin 自托管 Web Api 2 和 Autofac

问题描述:

我正在使用 Web Api 2、Owin 和 Autofac,需要一些指导.

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

概述
我有一个 Owin 自托管 Web Api,它使用 Autofac 进行 IoC 和依赖项注入.该项目是一个类似于服务的控制台应用程序,这意味着它可以停止和启动.我有一个带有两个构造函数的身份验证控制器:一个无参数,另一个注入存储库.

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.

问题
当我运行服务并调用 api 时,我的无参数构造函数被调用并且我的存储库永远不会被注入(_repository = null).

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

研究
我做了大量研究,并在 Github 上找到了一些有用的项目,我将它们复制到了 T 恤上,但我遗漏了大部分难题.很有帮助,但没有解决我的问题.我在 Stack Overflow 和 Dane Sparza 上阅读了这个问题有一个不错的 演示项目,但我找不到明确的解决方案.问题不在于自托管,而在于依赖注入.

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.

我的代码(细化解释)

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

程序.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();
               }
           }
       );
    }

API 控制器

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

您应该使用 HttpConfiguration,通过它可以在任何地方引导 OWIN.所以,这个:

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

GlobalConfiguration.Configuration.DependencyResolver = resolver;

应该变成:

config.DependencyResolver = resolver;

除此之外,一切看起来都不错.Api 控制器已注册,但您没有为它们提供范围.不确定 Autofac 范围是否默认为控制器的每个请求,或者它是否有每个请求范围的概念(我知道 LightInject 有).

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).

环顾四周,我认为您遵循了 Autofac 的 Google 代码存储库中的示例,它确实使用了 GlobalConfiguration.相反,如果您查看 GitHub 示例,有点不一样.尝试根据此进行更改.包括这个:

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 年更新

我上面所说的仍然适用,但 Autofac 的文档中有一些额外的内容(感谢 Brad):

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

OWIN 集成中的一个常见错误是使用了全局配置.配置.在 OWIN 中,您创建从头开始配置.你不应该参考使用 OWIN 时的 GlobalConfiguration.Configuration整合.

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.