Есть ли способ зарегистрировать и загрузить классы, реализующие интерфейс на один уровень выше в asp. net инъекция зависимостей ядра 2? - PullRequest
0 голосов
/ 08 мая 2020

У меня есть:

interface IExpirable{
   Expire(int id);
}

interface IExpirableA : IExpirable {
  MethodA();
}

interface IExpirableB : IExpirable {
  MethodB();
}

public class A : IExpirableA {//implement both methods Expire, and MethodA}
public class B : IExpirableB {//implement both methods Expire, and MethodB}

//in Startup.cs

services.AddScoped<IExpirableA , A>();
services.AddScoped<IExpirableB , B>();

//in HomeController.cs

private readonly IEnumerable<IExpirable> _expirables;
public HomeController(IEnumerable<IExpirable> expirables){
  expirables = _expirables;

    //here expirables is an empty array, however I would like it to contain instances of both A and B because
    // A -> IExpirableA -> Expirable and likewise for B.
    //is this something that can be accomplished without separately 
   //registering A and B with IExpirable each?
    }

Ответы [ 2 ]

1 голос
/ 08 мая 2020

Просто зарегистрируйте эти службы, используя его супер-интерфейс IExpirable будет работать

services.AddScoped<IExpirable, A>();
services.AddScoped<IExpirable, B>();

Однако будьте осторожны с этим конструктором

public HomeController(
  IEnumerable<IExpirable> expirables, 
  IExpirableA expirableA, 
  IExpirableB expirableB)
{ 

}

Если вы не хотите получать подобные исключения

InvalidOperationException: Unable to resolve service for type 'IExpirableA' while attempting to activate 'HomeController'.

Вероятно, вы можете улучшить таким образом

services.AddScoped<IExpirable, A>();
services.AddScoped<IExpirable, B>();
services.AddDerivedExpirables(ServiceLifetime.Scoped);

с помощью IServiceCollection extension

public static class ServiceCollectionExtension
{
    public static IServiceCollection AddDerivedExpirables(this IServiceCollection services, ServiceLifetime lifetime)
    {
        var scanAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
        var interfaceTypes = scanAssemblies.SelectMany(o => o.DefinedTypes
            .Where(x => x.IsInterface)
            .Where(x => x != typeof(IExpirable)) // exclude super interface
            .Where(x => typeof(IExpirable).IsAssignableFrom(x))
        );

        foreach (var interfaceType in interfaceTypes)
        {
            var types = scanAssemblies.SelectMany(o => o.DefinedTypes
                .Where(x => x.IsClass)
                .Where(x => interfaceType.IsAssignableFrom(x))
            );

            foreach (var type in types)
            {
                services.TryAdd(new ServiceDescriptor(interfaceType, type, lifetime));
            }
        }

        return services;
    }
}

Это расширение автоматически регистрирует все остальные интерфейсы, например IExpirableA или IExpirableB, производные от супер-интерфейса IExpirable . Попробуйте переключиться на свои нужды.

В OptionsServiceCollectionExtensions.cs есть пример, чтобы показать, как работает AddOptions () . Mayble полезно.

1 голос
/ 08 мая 2020

Вы можете использовать фабричные методы для внедрения одного и того же экземпляра в несколько интерфейсов.

services.AddScoped<IExpirableA, A>();
services.AddScoped<IExpirableB, B>();
services.AddScoped<IExpirable>(p => p.GetService<IExpirableA>());
services.AddScoped<IExpirable>(p => p.GetService<IExpirableB>());

И вы можете написать метод расширения, который использует отражение для обнаружения и регистрации всех реализованных интерфейсов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...