Простой инжектор: как зарегистрировать экземпляры обработчиков событий в диспетчере событий

У меня есть интерфейс.

// Assembly: Common
public interface IEventHandler<TEvent> where TEvent : IDomainEvent
{
    void HandleEvent(TEvent theEvent);
}

// Assembly: Common
public interface IDomainEvent
{
}

// Assembly: Common
public interface IEventDispatcher
{
    void Register(IEventHandler<IDomainEvent> handler);
    void Dispatch(IDomainEvent theEvent);
}

// Assembly: Membership
public sealed class MemberRegistered : IDomainEvent
{
    // event properties
}

Затем в моем проекте ASP MVC 5 у меня есть обработчик событий для событий.

public sealed class MemberRegisteredHandler : IEventHandler<MemberRegistered>
    {
        public MemberRegisteredHandler(ApplicationUserManager userManager)
        {
            this.userManager = userManager;
        }

        private ApplicationUserManager userManager;

        public void HandleEvent(MemberRegistered theEvent)
        {
            User user = new User(
                theEvent.MemberId.ToString(),
                theEvent.Username,
                theEvent.PersonalInformation.Email);

            this.userManager.CreateUserWithRandomPassword(user);
        }
    }

В моем классе Startup

var container = new Container();
container.RegisterManyForOpenGeneric(
               typeof(IEventHandler<>),
               container.RegisterAll,
               Assembly.GetExecutingAssembly());

container.Register<IEventDispatcher, SimpleEventDispatcher>();

При этом, как я могу получить event dispatcher и зарегистрировать в нем все обработчики событий?

я пытался

var eventDispatcher = container.GetInstance<IEventDispatcher>();

foreach (var handler in container.GetAllInstances<IEventHandler<IDomainEvent>>())
{
    eventDispatcher.Register(handler);
}

Но это не работает. метод GetAllInstances ничего не возвращает.

Things to note: IEventDispatcher зависит от моего конструктора EF DbContext, и у меня есть (прямо сейчас) два контекста. MembershipContext и IdentityAccessContext, оба производные от EventDipatchingContext, происходящие от DbContext. В основном это EventDispatchingContext : DbContext, затем MembershipContext : EventDispatchingContext

EventDispatchingContext отвечает за делегирование событий диспетчеру событий after its Commit() method is invoked. Они не увольняются мгновенно на моих объектах домена. Я получил эту архитектуру из блога Джимми Богарда.

https://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern/

Нужно ли в этом случае делать IEventDispatcher синглтоном? Почему?

Как правильно зарегистрировать обработчики в диспетчере событий?


person Jaime Sangcap    schedule 11.08.2015    source источник


Ответы (1)


Не «регистрируйте» обработчики событий в диспетчере событий. Вместо этого вы должны сделать реализацию диспетчера событий частью вашего корня композиции. Таким образом, вы можете безопасно получить зависимость от контейнера из диспетчера следующим образом:

public class SimpleInjectorEventDispatcher : IEventDispatcher {
    private readonly Container container;
    public SimpleInjectorEventDispatcher(Container container) {
        this.container = container;
    }

    public void Dispatch(IDomainEvent theEvent) {
        var handlerType = typeof(IEventHandler<>).MakeGenericType(theEvent.GetType());
        var handlers = this.container.GetAllInstances(handlerType);

        foreach (dynamic handler in handlers) {
            handler.HandleEvent((dynamic)theEvent);
        }
    }
}

Обратите внимание, что интерфейс IEventDispatcher теперь содержит только метод Dispatch.

Нужно ли в этом случае делать IEventDispatcher синглтоном? Почему?

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

person Steven    schedule 11.08.2015
comment
Это намного проще! Есть ли способ абстрагировать контейнер? - person Jaime Sangcap; 11.08.2015
comment
Конечно, но это обычно бесполезно для этого типа инфраструктурного компонента, который является частью корня вашей композиции. Но в любом случае вы можете внедрить в конструктор Func<Type, IEnumerable> вместо Container и зарегистрировать тип следующим образом: container.RegisterSingle<IEventDispatcher>(new SimpleEventDispatcher(container.GetAllInstances)). - person Steven; 11.08.2015
comment
есть ли недостаток, когда все классы, зависящие от IEventDispatcher, также станут синглтонами? На самом деле я не знаю преимуществ и недостатков использования синглетонов даже вне контекста внедрения зависимостей. - person Jaime Sangcap; 11.08.2015
comment
@Daskul: вы должны стремиться сделать свои графы объектов без состояния и передавать состояние через граф с помощью вызовов методов. В этом случае все ваши компоненты не будут иметь состояния и будут зависеть исключительно от синглетонов. В этом случае одни плюсы. Но, с другой стороны, если у вас есть компоненты с состоянием, создание их одноэлементными может вызвать состояние гонки и другие ошибки, связанные с многопоточностью. - person Steven; 11.08.2015
comment
Спасибо за информацию. Это мне очень помогает. есть еще что пожевать :) - person Jaime Sangcap; 11.08.2015