首页 > 解决方案 > 如何添加具有需要由 Autofac 解决的依赖项的自定义 ModelMetadataDetailsProvider?

问题描述

我正在尝试添加自定义 ModelMetadataDetailsProvider,但提供程序实现具有需要由服务提供程序 (Autofac) 解决的依赖项。如果我在 ConfigureServices 中添加 ModelMetadataDetailsProvider,我必须实例化并手动提供所有依赖项,其中一些是单例的并且是自动激活的,所以这不起作用...是否可以在 ConfigureServices 之外添加 ModelMetadataDetailsProvider?

看来这不能使用 DI 进行配置,所以我唯一能想到的就是在需要时使用 Service Locator Anti Pattern 来提供依赖项,而不是在构造函数中提供。有没有更可接受的方法来实现这一点?

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  services.AddMvc()
          .AddMvcOptions(options => {
             options.ModelMetadataDetailsProviders.Add(new MyProvider(???))
          })
          .SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
          .AddControllersAsServices();

  services.AddAutofac();

  ApplicationContainer = BuildContainer(services);

  return new AutofacServiceProvider(ApplicationContainer);
}

public IContainer BuildContainer(IServiceCollection services)
{
  var builder = new ContainerBuilder();
  builder.Populate(services);

  builder.RegisterType<HttpContextAccessor>()
      .As<IHttpContextAccessor>()
      .SingleInstance();

  builder.RegisterType<DataAccess>()
      .As<IDataAccess>()
      .WithParameter("connectionString", Configuration.GetConnectionString("DatabaseContext"))
      .InstancePerLifetimeScope();

   ....

   builder.RegisterType<D1>()
      .As<ID1>();

   builder.RegisterType<D2>()
      .As<ID2>();

   builder.RegisterType<D3>()
      .As<ID3>();

   builder.RegisterType<MyProvider>()
      .As<IMyProvider>();
 }



public interface IMyProvider : IDisplayMetadataProvider
{
  ...
}

public class MyProvider : IMyProvider
{
    public MyProvider (ID1 d1, ID2 d2, ID3 d3)
    {
      ...
    }

    public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
    {
      ...
    }
}

标签: asp.net-mvcasp.net-coredependency-injectionautofac

解决方案


IConfigureOptions<MvcOptions>您可以通过创建一个实现接口的类来实现这一点:

public class AddCustomModelMetadataDetailsProvider : IConfigureOptions<MvcOptions>
{
    private readonly MyCustomModelMetadataDetailsProvider _provider;

    public AddCustomModelMetadataDetailsProvider(MyCustomModelMetadataDetailsProvider provider)
    {
        _provider = provider;
    }

    public void Configure(MvcOptions options)
    {
        options.ModelMetadataDetailsProviders.Add(_provider);
    }
}

并在Configure方法中注册它:

services.AddTransient<IConfigureOptions<MvcOptions>, AddCustomModelMetadataDetailsProvider>();

如您所见,这种方法的好处是您可以在AddCustomModelMetadataDetailsProvider类中使用常规构造函数注入来获取您感兴趣的服务的实例。

ASP.NET 自动调用容器中注册Configure的所有服务的方法。IConfigureOptions<MvcOptions>


因为创建这些类可能很耗时,所以 ASP.NET Core 2.2 引入了新的重载,允许您执行以下操作:

services
    .AddOptions<MvcOptions>()
    .Configure<MyCustomModelMetadataDetailsProvider>((options, customMetadataDetailsProvider) =>
    {
         options.ModelMetadataDetailsProviders.Add(customMetadataDetailsProvider);
    });

在这种情况下,customMetadataDetailsProvider将从容器中解析。

您最多可以包含 5 个服务来配置您的选项。请参阅此官方文档页面


推荐阅读