首页 > 解决方案 > Autofac 无法解析 ASP。NET Core Controller 尝试传递参数进行注册时

问题描述

我们一直使用 Autofac 作为依赖注入的 IoC 容器。为了在注册期间将参数传递给 Web API 控制器,我们希望将其传递给 Controller 构造函数。它是一个双类型参数,名称ratio在下面的构造函数中。

但是,Autofac 会抛出异常,因为它无法解析 double 类型的最后一个参数(ratio此处)并且无法创建DataProcessController.

public DataProcessController(ILogger logger, IDataProvider dataProvider, double ratio)
{
  ...
}

通过 usingWithParameter方法来指定要使用的值。

builder.RegisterType<DataProcessController>().AsSelf()
            .WithParameter("ratio", 0.4);

我还调用AddControllersAsServices以将控制器注册为服务并从容器中解析它。

services.AddMvc()
        .AddControllersAsServices()
        .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

Populate()在完成注册后打电话。

builder.Populate(services);
return new AutofacServiceProvider(builder.Build());

全部在下面的函数中定义:

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddOptions();
        services.AddHttpClient();
        services.AddMvc()
            .AddControllersAsServices()
            .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

        //Autofac
        var builder = new ContainerBuilder();
        builder.RegisterInstance(_logger).As<ILogger>().SingleInstance();

        //All other dependencies are defined here....

        builder.RegisterType<DataProcessController>().AsSelf()
            .WithParameter("ratio", 0.4);

        // auto-discover examples from this assembly
        services.AddSwaggerExamplesFromAssemblyOf<Startup>();

        builder.Populate(services);
        return new AutofacServiceProvider(builder.Build());
    }

我尝试将原始数据类型和引用数据类型(如另一个类)作为容器来保存双精度值,但都未能通过 Autofac 解析控制器。

标签: asp.net-coreautofac

解决方案


您需要builder.Populate(services)在将内容添加到要覆盖的容器之前调用。

如果您查看有关新产品如何IServiceProviderFactory工作的文档,则基本上是:

  • 用来ConfigureServices注册东西IServiceCollection
  • 在服务提供者工厂的幕后,a被创建并为您ContainerBuilder调用builder.Populate(services)
  • 用于ConfigureContainer直接向 Autofac 注册事物
  • 在服务提供者工厂的幕后,最终的容器被构建并放入一个IServiceProvider

在您AutofacServiceProvider自己构建的旧集成中,您必须注意顺序。它仍然是 Autofac - 最后获胜。

当你这样做时services.AddControllersAsServices(),就像在说builder.RegisterType<DataProcessController>().AsSelf()......但它实际上还没有与建造者交谈。这就像一个“存储的注册”,在您调用之前不会“回放” Populate

因此,如果您简化注册并按照它们实际执行的顺序排列:

// These will NOT be executed until builder.Populate()
// services.AddOptions();
// services.AddHttpClient();
// services.AddMvc()
//    .AddControllersAsServices()
//    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);

var builder = new ContainerBuilder();
builder.RegisterInstance(_logger).As<ILogger>().SingleInstance();
builder.RegisterType<DataProcessController>().AsSelf()
    .WithParameter("ratio", 0.4);

// Still not executed until builder.Populate()
services.AddSwaggerExamplesFromAssemblyOf<Startup>();

// Here goes everything!
// - AddOptions => ContainerBuilder
// - AddHttpClient => ContainerBuilder
// - AddMvc => ContainerBuilder
// - AddControllersAsServices => ContainerBuilder
// ---- OH NO! JUST OVERWROTE THE WITHPARAMETER REGISTRATION!
// - AddSwaggerExamplesFromAssemblyOf => ContainerBuilder
builder.Populate(services);

return new AutofacServiceProvider(builder.Build());

这是我的建议:

最佳案例:转向服务提供商工厂支持

在文档中,这是在 ASP.NET 1.1-2.2 中您不自己构建 AutofacServiceProvider 而是在 web 主机级别注册 Autofac 的地方。这是最佳选择,因为它可以让您在准备好后为升级到 ASP.NET Core 3.0 做好更多准备。一旦你达到 3.0,你无论如何都必须这样做。

后备选项:组织您的注册

如果你不能切换集成来强制你使用单独的ConfigureServicesConfigureContainer方法,至少要组织好代码,ConfigureServices这样你就做好了准备。这也将确保以正确的顺序调用所有内容,以避免现在和将来出现此问题。

  • services.AddWhatever()将所有注册分组IServiceCollection在顶部。例如,不要services.AddSwaggerExamplesFromAssembly在 Autofac 注册中流连忘返。
  • 创建ContainerBuilder并立即调用Populate。不要在这两件事之间进行任何注册。
  • 通话后将所有builder.Register<T>()注册分组。ContainerBuilderbuilder.Populate
  • 像现在一样,将服务提供者的创建作为方法中的最后一件事。

推荐阅读