IEnumerable泛型的依赖注入

问题描述 投票:0回答:2

我正在尝试优化我的代码,以注入一系列实现接口的类。IEventHandler<TEvent>

我具有以下结构:

public interface IEventHandlerMarker    {   }

public interface IEventHandler<in TEvent> : IEventHandlerMarker where TEvent : IEvent
{
    Task Handle(TEvent eventItem);
}

public interface IEvent
{
    public DateTime Timestamp { get; set; }
}

我在DI中注册了标记接口IEventHandlerMarker,并且在访问处理程序时,目前正在执行以下操作:

public EventPublisherService(IEnumerable<IEventHandlerMarker> eventHandlers)
{
    // Check and and all event handlers
    foreach (IEventHandlerMarker item in eventHandlers)
    {
        AddEventHandler(item);
    }
}

在我的AddEventHandler方法中,我将其过滤为IEventHandler<>,如下所示:

Type handlerType = eventHandlerMarker.GetType().GetInterfaces()
    .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEventHandler<>));

到目前为止,一切正常,但是我想摆脱标记接口和过滤器逻辑。因此,我将处理程序的注册更改为以下方法,这似乎按预期工作:

public static IServiceCollection AddEventHandlers(this IServiceCollection serviceDescriptors, params Assembly[] handlerAssemblies)
{
    Type genericHandlerType = typeof(IEventHandler<>);
    foreach (var implementationType in genericHandlerType.GetTypesWithGenericInterfacesInAssemblies(handlerAssemblies))
    {
        Type interfaceType = implementationType.GetGenericInterfaceType(genericHandlerType);
        serviceDescriptors.AddSingleton(interfaceType, implementationType);
    }
    return serviceDescriptors;
}

public static List<Type> GetTypesWithGenericInterfacesInAssemblies(this Type source, params Assembly[] assemblies)
{
    return assemblies
        .SelectMany(currentAssembly => currentAssembly.GetTypes()
        .Where(type => type.GetInterfaces().Any(
                interfaceItem => interfaceItem.IsGenericType
                && interfaceItem.GetGenericTypeDefinition().Equals(source))))
        .ToList();
}

我将EventPublisherService的构造函数更改为以下内容:

public EventPublisherService(IServiceProvider serviceProvider)
{
    Type ienumerableOfIEventHandlerType = typeof(IEnumerable<>).MakeGenericType(typeof(IEventHandler<>));
    object result = serviceProvider.GetService(ienumerableOfIEventHandlerType);
}

但是结果总是显示为空。我在Google上搜索并检查了一些关于Stackoverflow的文章,并发现了以下文章:https://stackoverflow.com/a/51504151/1099519

我不确定这是否是同一情况,因为我没有使用工厂。

使用的版本:.NET Core 3.1和Autofac 4.9.4,用于依赖项注入管理。

c# generics .net-core dependency-injection autofac
2个回答
0
投票

使用autofac,您可以注入IEnumerable<IEventHandler<TEvent>>,而Autofac应该解析其所有实现的列表。

https://autofaccn.readthedocs.io/en/latest/resolve/relationships.html


0
投票

question/answer中所示自动注册所有处理程序:

builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
   .AsClosedTypesOf(typeof (IEventHandler<>)).AsImplementedInterfaces();

[当您拥有TEvent并想查找所有处理程序时,请通过构造具体的接口类型as follows来获取它们:

Type generic = typeof(IEnumerable<IEventHandler<>>);
Type[] typeArgs = { typeof(TEvent) }; // you might get the Type of TEvent in a different way
Type constructed = generic.MakeGenericType(typeArgs);

您应该将此内容缓存在字典中,以避免在每次事件分发时进行反射。

一旦构造了具体的接口类型,就可以向Autofac要求该接口的所有实现:

var handlers = container.Resolve(constructed);

现在,问题在于,对于处理程序实例,您只能使用Invoke(反射)调用Handle方法。这是一个性能问题,但与您注册和解析处理程序的方式无关。这与您需要从对象调用具体方法这一事实有关。为了避免使用反射,您需要调用具体方法的编译代码(请注意,使用泛型还会为每种类型创建具体的编译代码)。

我可以想到两种获取编译代码来进行此调用的方法:

  1. 手动编写一个委托,该委托将您的对象处理程序实例转换为您拥有的每个TEvent类型的具体类型。然后将所有这些委托存储在字典中,以便您可以在运行时基于TEvent类型找到它们,并通过处理程序实例和事件实例对其进行调用。使用这种方法,对于您创建的每个新的TEvent,都需要创建一个匹配的委托。

  2. 进行与以前类似的操作,但在启动时发出代码。因此,同样的事情,但是代表的整个创建是自动的。

© www.soinside.com 2019 - 2024. All rights reserved.