Как добавить пользовательский ModelMetadataDetailsProvider, имеющий зависимость, которая должна быть разрешена с помощью Autofac? - PullRequest
0 голосов
/ 15 января 2019

Я пытаюсь добавить пользовательский ModelMetadataDetailsProvider, но реализация провайдера имеет зависимости, которые должны быть разрешены провайдером услуг (Autofac). Если я добавлю ModelMetadataDetailsProvider в ConfigureServices, мне придется создать и вручную предоставить все зависимости, некоторые из которых являются одноэлементными и активируются автоматически, так что это не будет работать ... Можно ли добавить ModelMetadataDetailsProvider за пределами ConfigureServices?

Не похоже, что это можно настроить с помощью DI, поэтому единственное, о чем я могу подумать, - это использовать шаблон Anti-Locator для предоставления зависимостей, когда они нужны, а не в конструкторе. Есть ли более приемлемый способ сделать это?

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

1 Ответ

0 голосов
/ 16 января 2019

Этого можно добиться, создав класс, реализующий интерфейс 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 служб для настройки своих параметров. Смотрите эту страницу официальной документации .

...