简体   繁体   English

Autofac:解决 ApiControllers 的依赖关系

[英]Autofac: Resolving dependencies for ApiControllers

I'm new to Inversion of Control & Autofac.我是控制反转和 Autofac 的新手。 Going through the documentation, there is a lot ASP.NET Web Api concepts I do not fully understand or have experience with, making it a bit difficult to determine what is missing from my implementation.浏览文档,有很多 ASP.NET Web Api 概念我不完全理解或有经验,因此很难确定我的实现中缺少什么。

I want to apply Autofac to an existing ASP.NET Web Api project that has several ApiControllers.我想将 Autofac 应用于具有多个 ApiControllers 的现有 ASP.NET Web Api 项目。 These controllers all share a parent abstract class.这些控制器都共享一个父抽象 class。 This abstract class has a method that is responsible for returning an instance of a service.这个抽象的 class 有一个负责返回服务实例的方法。 I hoped to replace this method with Autofac's dependency injection.我希望用 Autofac 的依赖注入来替换这个方法。

The parent abstract class from which every ApiController inherits, is very simple.每个 ApiController 继承的父抽象 class 非常简单。

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

    public BaseApiController() {}

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

Every controller inherits from the above class, while some employs a default Get method, most have multiple routes.每个 controller 都继承自上面的 class,而有些采用默认的 Get 方法,大多数有多个路由。 Not a single controller is specifying a constructor:没有一个 controller 指定构造函数:

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 is integrated into the Application_Start method of the applications Glabal.asax.cs, registering the ServerClientProvider which should be the provider that should be resolved to when a dependency to the IMyService is encountered: 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);

    }
}

I have configured the ASP.NET application, in the web.config:我在 web.config 中配置了 ASP.NET 应用程序:

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

As I understand it, Autofac should automatically resolve the provider when encountering a controller with a public property that matches the registered type (Property Injection), or if a constructor exists with a parameter that matches any registered dependency.据我了解,当遇到具有与注册类型(属性注入)匹配的公共属性的 controller 时,或者如果存在具有与任何注册依赖项匹配的参数的构造函数,Autofac 应该自动解析提供程序。

However, I'm not getting any errors relating to my Autofac configuration.但是,我没有收到与我的 Autofac 配置相关的任何错误。 I'm getting a NullReferenceException when the MyController attempt to call the IMyService.FooBar method.MyController尝试调用IMyService.FooBar方法时,我收到 NullReferenceException。

What am I missing?我错过了什么?

Any help would be greatly appreciated.任何帮助将不胜感激。

After several attempts to debug the code, I realized that there existed another start up implementation in the project.在多次尝试调试代码后,我意识到项目中存在另一个启动实现。 OWIN is used and the start-up implementation I was trying to integrate Autofac with is instantiated but never used.使用了 OWIN,并且我试图将 Autofac 与之集成的启动实现被实例化但从未使用过。 So following the instructions on https://autofac.readthedocs.io/en/latest/integration/owin.html , yielded the results I was looking for.因此,按照https://autofac.readthedocs.io/en/latest/integration/owin.html上的说明,得到了我正在寻找的结果。

Here is how the start-up implemetation should look at:以下是启动实现应该如何看待:

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

The controller & abstract class it inherited from, were tweaked, removing the parameterless constructors.对其继承的 controller 和抽象 class 进行了调整,删除了无参数构造函数。

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

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

Every controller inherits from the above class, while some employs a default Get method, most have multiple routes.每个 controller 都继承自上面的 class,而有些采用默认的 Get 方法,大多数有多个路由。 Not a single controller is specifying a constructor:没有一个 controller 指定构造函数:

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

Thanks for the interest, hopefully my rookie mistake will help another rookie.感谢您的关注,希望我的菜鸟错误能帮助另一个菜鸟。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM