Autofac解析抽象基类的所有EventHandler

时间:2015-05-04 14:13:42

标签: c# autofac

嗨有些我怎么找不到这个问题的答案: 我有事件处理程序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();

handlers变量为空... 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. 解析为IEnumerable的EventHandler
  3. 的实现

2 个答案:

答案 0 :(得分:1)

您想将MyEvenHandler投射到IEventHandler<IDomainEvent>

如果我们尝试以下代码:

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

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

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

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

如果您需要所有eventHandler的列表,则必须引入基本接口

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<MyEvent>

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

顺便说一下,IEnumerable<IEventHandler>可能依赖于messageDispatcher,而不是依赖于ILifetimeScope,而且当它需要eventHandlers时,它将能够解决它们:

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;
        }
    }
}

编辑:这个答案是在使用完整界面声明进行编辑之前的

如果IEventHandler<TEvent>不接受任何TEvent方法,则必须使用IEventHandler<TEvent>修饰符将out转换为协变界面(请参阅{{3} }了解更多信息)。

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

使用此功能,CLR将能够MyEventHandler投射到IEventHandler<DomainEventBase>

然后,您必须通过将其注册为IEventHandler<DomainEventBase>

告诉 Autofac 您的类型为IEventHandler<DomainEventBase>
builder.RegisterAssemblyTypes(typeof(Program).Assembly)
       .As<IEventHandler<DomainEventBase>>();

您现在可以使用

获取所有的eventHandler
container.Resolve<IEnumerable<IEventHandler<DomainEventBase>>>()

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

答案 1 :(得分:0)

1,创建一个没有通用

的空接口
public interface IEventHandler{}

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

IEventHandler<TEvent>:IEventHandler

3,尝试解决使用此界面

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

这种方式可能不是最好的