【问题标题】:Autofac resolve all EventHandlers of abstract base classAutofac解析抽象基类的所有EventHandler
【发布时间】:2015-05-04 14:13:42
【问题描述】:

嗨,我怎么找不到这个问题的答案: 我有事件处理程序ala:

public class MyEvenHandler : EventHandler<MyEvent>

EventHandler 是一个抽象类

 public abstract class EventHandler<TEvent> : IEventHandler<TEvent>
    where TEvent : IDomainEvent 

public interface IEventHandler<in TEvent> : IEventHandler where TEvent : IDomainEvent
{
    bool Handles(IDomainEvent @event);
    void Handle(TEvent @event);
    Task HandleAsync(TEvent @event);
}

我这样注册 autofac:

var builder = new ContainerBuilder();

builder.RegisterSource(new ContravariantRegistrationSource());

...

builder.RegisterAssemblyTypes(commandsAssemblies)
            .AsClosedTypesOf(typeof(EventHandler<>))
            .AsImplementedInterfaces()
            .InstancePerRequest();

现在我想解析所有事件处理程序并将它们注册到 messageDispatcher 类

var handlers = e.Context.Resolve<IEnumerable<IEventHandler<IDomainEvent>>>().ToList();

var handlers2 = e.Context.Resolve<IEnumerable<IEventHandler<PolicyCreated>>>().ToList();

处理程序变量为空... handlers2 正确解析。但我想通用解决所有处理程序

messageDispatcher (eventDispathcer) 看起来像这样:

public class EventDispatcher : IEventDispatcher
{
    private readonly IList<IEventHandler> _eventHandlers = new List<IEventHandler>();

    public virtual void RegisterEventHandler(IEventHandler eventHandler)
    {
        _eventHandlers.Add(eventHandler);
    }

    public virtual IMessageResults Publish<TEvent>(TEvent @event) where TEvent : IDomainEvent
    {
        var result = new MessageResults();
        var handlers = _eventHandlers;

        if (handlers == null)
        {
            Trace.WriteLine(String.Format("No event handlers for event {0} ", typeof(TEvent)));

            result.AddResult(new MessageResult(true));
            return result;
        }

        foreach (var eventHandler in handlers.Where(h => h.Handles(@event as IDomainEvent)))
        {
            eventHandler.Handle(@event);
        }
        return result;
    }

    public int EventHandlerCount
    {
        get
        {
            return _eventHandlers.Count();
        }
    }
}

总结目标:

  1. 使用程序集扫描
  2. 解析为 EventHandler 实现的 IEnumerable

【问题讨论】:

    标签: c# autofac


    【解决方案1】:

    您想将MyEvenHandler 转换为IEventHandler&lt;IDomainEvent&gt;

    如果我们尝试以下代码:

    MyEventHandler handler = new MyEventHandler();
    IEventHandler<IDomainEvent> e = (IEventHandler<IDomainEvent>)handler;
    

    CLR 将抛出 InvalidCastException,因为 IEventHandler&lt;TDomainEvent&gt; 不是协变的。如果 CLR 允许这种转换,则意味着以下代码可以编译:

    MyEventHandler handler = new MyEventHandler();
    IEventHandler<IDomainEvent> e = (IEventHandler<IDomainEvent>)handler;
    e.Handle(new MyEvent2()); 
    

    CLR 应该如何执行它? e 期望 MyEvent 而不是 MyEvent2

    如果你想要一个所有事件处理程序的列表,你必须引入一个基本接口

    public interface IEventHandler
    {
        Boolean Handles(IDomainEvent @event);
    }
    public interface IEventHandler<TEvent> : IEventHandler 
        where TEvent : IDomainEvent
    {
        void Handle(TEvent @event);
        Task HandleAsync(TEvent @event);
    }
    

    并将 eventHandlers 注册为IEventHandler

    ContainerBuilder builder = new ContainerBuilder();
    builder.RegisterAssemblyTypes(typeof(Program).Assembly)
           .AsClosedTypesOf(typeof(IEventHandler<>))
           .As<IEventHandler>();
    

    使用此注册,您将能够解析IEventHandlerIEventHandler&lt;MyEvent&gt;

    var genericHandlers = container.Resolve<IEnumerable<IEventHandler>>();
    var handlers = container.Resolve<IEnumerable<IEventHandler<MyEvent>>>();
    

    顺便说一句,messageDispatcher 可能依赖于ILifetimeScope,而不是依赖于IEnumerable&lt;IEventHandler&gt;,当它需要事件处理程序时,它将能够解决它们:

    public class EventDispatcher
    {
        private readonly ILifetimeScope _scope;
    
        public EventDispatcher(ILifetimeScope scope)
        {
            this._scope = scope;
        }
    
        public virtual IMessageResults Publish<TEvent>(TEvent @event) where TEvent : IDomainEvent
        {
            var result = new MessageResults();
            var handlers = this._scope.Resolve<IEnumerable<IEventHandler<TEvent>>>().ToList();
    
            if (!handlers.Any())
            {
                Trace.WriteLine(String.Format("No event handlers for event {0} ", typeof(TEvent)));
    
                result.AddResult(new MessageResult(true));
            }
            else
            {
                foreach (var eventHandler in handlers.Where(h => h.Handles(@event as IDomainEvent)))
                {
                    eventHandler.Handle(@event);
                }
            }
            return result;
        }
    
        public int EventHandlerCount
        {
            get
            {
                // not tested 
                var handlerCount = this._scope.ComponentRegistry
                                              .Registrations
                                              .Where(r => r.Services
                                                         .OfType<IServiceWithType>()
                                                         .Any(swt => swt.ServiceType.IsGenericType
                                                                     && swt.ServiceType.GetGenericTypeDefinition() == typeof(IEventHandler<>)))
                                              .Count();
                return handlerCount;
            }
        }
    }
    

    EDIT :这个答案是在完整接口声明的编辑之前

    如果IEventHandler&lt;TEvent&gt; 在方法中不接受任何TEvent,则必须使用out 修饰符将IEventHandler&lt;TEvent&gt; 转换为协变接口(有关详细信息,请参阅out (Generic Modifier) (C# Reference))。

    public interface IEventHandler<out TEvent>
        where TEvent : DomainEventBase
    { }
    

    使用它,CLR 将能够将MyEventHandler 转换为IEventHandler&lt;DomainEventBase&gt;

    然后,你必须告诉 Autofac 你的类型是IEventHandler&lt;DomainEventBase&gt;,方法是将它们注册为IEventHandler&lt;DomainEventBase&gt;

    builder.RegisterAssemblyTypes(typeof(Program).Assembly)
           .As<IEventHandler<DomainEventBase>>();
    

    您现在可以使用

    获取所有事件处理程序
    container.Resolve<IEnumerable<IEventHandler<DomainEventBase>>>()
    

    顺便说一句,您的情况不需要ContravariantRegistrationSource

    【讨论】:

    • 谢谢回答! IEventHandler 将 TEvent 作为其 Handle 方法中的输入参数,因此不能将其声明为协变。我现在已经添加了完整的声明
    • @ChristianJohansen 查看我的编辑。顺便说一句,你能分享你的 messageDispatcher 吗?可能有事要做
    • 当然!我现在已经添加了 messageDispathcer (EventDispatcher) 的代码。我目前的工作是将注册和解析为简单的非通用接口 IEventHandler 限制我自己只使用接口。
    • @ChristianJohansen 在您的情况下,我将在 EventDispatcher 类上添加对 ILifetimeScope 的依赖项。然后可以在您的 Publish 方法中解析最终类型。
    • 所以在测试期间我必须模拟 ILifeTimeScope 吗? Mybe这不会很难。但我宁愿不注入它。
    【解决方案2】:

    1、创建一个没有泛型的空接口

    public interface IEventHandler{}
    

    2、你的IEventHandler应该实现这个接口

    IEventHandler<TEvent>:IEventHandler
    

    3、尝试解析使用该接口

    e.Context.Resolve<IEnumerable<IEventHandler>>()
    

    这种方式可能不是最好的

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-04
      • 2014-01-17
      • 1970-01-01
      • 2018-03-06
      相关资源
      最近更新 更多