Autofac:解决 ApiController 的依赖关系

Autofac: Resolving dependencies for ApiControllers

我是控制反转和 Autofac 的新手。通过文档,有很多 ASP.NET Web Api 概念我不完全理解或没有经验,因此很难确定我的实现中缺少什么。

我想将 Autofac 应用于现有的 ASP.NET Web Api 项目,该项目有多个 Api 控制器。这些控制器都共享一个父抽象 class。这个抽象 class 有一个方法负责返回一个服务的实例。我希望用Autofac的依赖注入来代替这个方法。

每个 ApiController 继承的父抽象 class 非常简单。

public abstract class BaseApiController
{
    public IMyService serviceClient { get; set; }

    public BaseApiController() {}

    public BaseApiController(IMyService serviceClient)
    {
        this.serviceClient = serviceClient;
    }
}

每个控制器都继承自上述class,而有些控制器使用默认的Get 方法,大多数有多个路由。没有一个控制器指定构造函数:

public class MyController : BaseApiController
{
    public MyController() : base() {}
    public MyController(IMyService serviceClient) : base(serviceClient) {}

    [HttpGet]
    [Route("api/foo/bar")]
    [ActionName("FooBar")]
    public string FooBar()
    {
        using (serviceClient)
        {
            return serviceClient.GetFooBar() as string;
        }
    }
}

Autofac 已集成到应用程序 Glabal.asax.cs 的 Application_Start 方法中,注册 ServerClientProvider,它应该是遇到对 IMyService 的依赖时应解析到的提供程序:

public class Global : System.Web.HttpApplication, IContainerProviderAccessor
{
    #region AutoFac
    private static IContainerProvider _containerProvider;

    public IContainerProvider ContainerProvider => _containerProvider;
    #endregion AutoFac

    protected void Application_Start(object sender, EventArgs e)
    {

        var builder = new ContainerBuilder();

        builder.Register(x => ServiceClientProvider.GetServiceClient())
            .As<IMyService>()
            .InstancePerRequest();

        builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
        builder.RegisterHubs(Assembly.GetExecutingAssembly()).PropertiesAutowired();

        var container = builder.Build();

        GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
        GlobalHost.DependencyResolver = new AutofacDependencyResolver(container);

        _containerProvider = new ContainerProvider(container);

    }
}

我已经配置了 ASP.NET 应用程序,在 web.config:

<configuration>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
      <add name="ContainerDisposal" type="Autofac.Integration.Web.ContainerDisposalModule, Autofac.Integration.Web" preCondition="managedHandler" />
      <add name="PropertyInjection" type="Autofac.Integration.Web.Forms.PropertyInjectionModule, Autofac.Integration.Web" preCondition="managedHandler" />
    </modules>
  </system.webServer>
<configuration>

据我了解,Autofac 应该在遇到具有匹配注册类型(属性 注入)的 public 属性 的控制器时自动解析提供程序,或者如果构造函数存在一个与任何已注册依赖项相匹配的参数。

但是,我没有收到与我的 Autofac 配置相关的任何错误。 MyController 尝试调用 IMyService.FooBar 方法时出现 NullReferenceException。

我错过了什么?

如有任何帮助,我们将不胜感激。

经过多次调试代码,我意识到项目中存在另一个启动实现。使用了 OWIN 并且我尝试将 Autofac 与之集成的启动实现被实例化但从未使用过。所以按照 https://autofac.readthedocs.io/en/latest/integration/owin.html 上的说明,得到了我想要的结果。

以下是启动实施应该如何看待:

[assembly: OwinStartup(typeof(MyWebApp.Api.StartUp))]

namespace MyWebApp.Api
{
    public class StartUp
    {

        public void Configuration(IAppBuilder app)
        {
            // In OWIN you create your own HttpConfiguration rather than
            // re-using the GlobalConfiguration.
            HttpConfiguration httpconfig = new HttpConfiguration();

            httpconfig.Routes.MapHttpRoute(
                name: "ApiRouteWithAction",
                routeTemplate: "api/{controller}/{action}");

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

            // Autofac SignalR integration
            var builder = new ContainerBuilder();

            // Register Web API controller in executing assembly.
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

            // Register SignalR hubs.
            builder.RegisterHubs(Assembly.GetExecutingAssembly());

            // Registering Deletegates
            builder.Register(x => ServiceClientProvider.GetServiceClient())
                .As<IMyService>()
                .InstancePerRequest();

            // Set the dependency resolver to be Autofac.
            var container = builder.Build();
            httpconfig.DependencyResolver = new AutofacWebApiDependencyResolver(container);

            app.UseWebApi(httpconfig);

            // Register the Autofac middleware FIRST, then the custom middleware if any.
            app.UseAutofacMiddleware(container);
        }
    }
}

对其继承的控制器和抽象 class 进行了调整,删除了无参数构造函数。

public abstract class BaseApiController
{
    public IMyService serviceClient { get; set; }

    public BaseApiController(IMyService serviceClient)
    {
        this.serviceClient = serviceClient;
    }
}

每个控制器都继承自上面的class,而有些控制器使用默认的Get方法,大多数有多个路由。没有一个控制器指定构造函数:

public class MyController : BaseApiController
{
    public MyController(IMyService serviceClient) : base(serviceClient) {}

    [HttpGet]
    [Route("api/foo/bar")]
    [ActionName("FooBar")]
    public string FooBar()
    {
        using (serviceClient)
        {
            return serviceClient.GetFooBar() as string;
        }
    }
}

感谢您的关注,希望我的菜鸟错误能帮助到另一个菜鸟。