2015-05-04 3 views
0

Привет Некоторые, как я не могу найти ответ на этот вопрос: У меня есть обработчик события ала:Autofac решить все EventHandlers абстрактного базового класса

public class MyEvenHandler : EventHandler<MyEvent> 

Где EventHandler является абстрактным классом

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

И

Я зарегистрированный autofac:

var builder = new ContainerBuilder(); 

builder.RegisterSource(new ContravariantRegistrationSource()); 

...

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

Теперь я хочу, чтобы разрешить все eventhandlers и зарегистрировать их с messageDispatcher класса

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

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

переменная Обработчики пуста ... handlers2 решает правильно , Но я хотел бы разрешить все обработчики genericly

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

ответ

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 должен выполнить его?е ожидать MyEvent и не MyEvent2

Если вы хотите получить список всех ваших eventHandlers вы должны ввести основной интерфейс

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>(); 

С помощью этого регистрации, вы сможете решить IEventHandler и IEventHandler<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; 
     } 
    } 
} 

EDIT : Этот ответ был до редактирования с полной декларацией интерфейса

Если IEventHandler<TEvent> не принимает TEvent в методах, y ou необходимо преобразовать ваш IEventHandler<TEvent> в ковариантный интерфейс, используя модификатор out (см. out (Generic Modifier) (C# Reference) для получения дополнительной информации).

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

Используя это, CLR будет в состоянии бросить MyEventHandler в IEventHandler<DomainEventBase>.

Тогда вы должны сказать Autofac что ваши типов IEventHandler<DomainEventBase> путем регистрации их в качестве IEventHandler<DomainEventBase>

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

Теперь вы можете получить все ваши eventHandlers используя

container.Resolve<IEnumerable<IEventHandler<DomainEventBase>>>() 

К слову, ContravariantRegistrationSource не нужен в вашем случае.

+0

Thx для ответа! IEventHandler принимает TEvent в качестве входного параметра в свой метод Handle, поэтому он не может быть объявлен как ковариантный. Теперь я добавил полную декларацию –

+0

@ChristianJohansen посмотрю мои правки. Кстати, вы могли бы поделиться своим сообщениемDispatcher? Может быть что-то делать –

+0

Конечно! Я добавил код для messageDispatcer (EventDispatcher) сейчас. Моя работа в настоящее время - это регистрация и разрешение как простой не общий интерфейс IEventHandler, ограничивающий мою работу только работой с интерфейсами. –

0

1, создайте пустой интерфейс без родового

public interface IEventHandler{} 

2, ваш IEventHandler должен реализовать этот интерфейс

IEventHandler<TEvent>:IEventHandler 

3, Попробуйте использовать Resolve этот интерфейс

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

этот путь могут быть не самыми лучшими

Смежные вопросы