Как получить все общие реализации из ServiceProvider в Asp.Net Ядро

#c# #asp.net-core

#c# #asp.net-core

Вопрос:

Я пытаюсь получить все общие реализации общего интерфейса и зарегистрировать все из них, используя не общий интерфейс, как показано ниже:

 collection.AddSingleton(typeof(IEventDispatcher<>), typeof(EventDispatcher<>));
collection.AddSingleton(p =>
{ 
    return p.GetServices(typeof(IEventDispatcher<>)).Select(x => x as IEventDispatcher);
});

public interface IEventDispatcher
{
    Task HandleAsync();
}

public interface IEventDispatcher<in T> : IEventDispatcher where T : IEvent<T>
{
    void Dispatch(T value); 
}
  

Причина, по которой я это делаю, заключается в том, что мне нужны все диспетчеры событий, введенные в фоновый запуск задания, который не знает общих параметров.

 public class EventJobRunner : IBackgroundJob
{
    private readonly IEnumerable<IEventDispatcher> _queueHandlers;

    public EventJobRunner(IEnumerable<IEventDispatcher> queueHandlers)
    { 
        _queueHandlers = queueHandlers;
    }
    
    public TimeSpan Interval => TimeSpan.FromSeconds(10);

    public async Task PerformAsync(CancellationToken cancellationToken)
    {
        var tasks = _queueHandlers.Select(x => x.HandleAsync());
        await Task.WhenAll(tasks);
    }
    
    public void Dispose()
    {
    }
}
  

Я понимаю, что извлечение открытых обобщений так, как я это делаю, невозможно, но я не могу найти правильное решение этой простой проблемы.

Есть ли какой-либо способ реорганизовать мой код, чтобы сделать это возможным?

Ответ №1:

Я нашел решение, которое отлично работает, даже если это не самое динамичное решение.

Учитывая, что у меня есть набор классов IEvent, я уверен, что количество IEventDispacher должно быть таким же, как и экземпляров IEvent. Итак, у меня есть один диспетчер для каждого типа события. Поэтому я мог бы просто получить все реализации IEvent и добавить все соответствующие диспетчеры для каждой из них. Код можно найти ниже.

 var eventTypes = TypeUtility.GetImplementationsOfGeneric(Assembly.GetExecutingAssembly(), typeof(IEvent<>));

foreach (var eventType in eventTypes)
{
    var instanceType = typeof(EventDispatcher<>).MakeGenericType(eventType);
    var interfaceType = typeof(IEventDispatcher<>).MakeGenericType(eventType);
    
    collection.AddSingleton(interfaceType, instanceType);
    collection.AddSingleton(typeof(IEventDispatcher), p => p.GetRequiredService(interfaceType));
} 
  

GetImplementationsOfGeneric это просто вспомогательный метод, который поможет мне получить все реализации интерфейса IEvent. Ниже также можно найти реализацию.

     public static IEnumerable<TypeInfo> GetImplementationsOfGeneric<TInterface>(Assembly assembly)
    {
        var interfaceType = typeof(TInterface);

        if (!interfaceType.IsGenericType)
            throw new InternalException($"Type: {interfaceType.FullName} should be a generic interface.");

        var types = assembly
            .DefinedTypes
            .Where(x => x.GetInterfaces()
                            .Any(i => i.IsGenericType amp;amp;
                                      i.GetGenericTypeDefinition() == interfaceType)
                        amp;amp; !x.IsAbstract amp;amp; !x.IsInterface);

        return types;
    }
  

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