Использование Autofac с событиями домена - PullRequest
14 голосов
/ 12 апреля 2011

Я пытаюсь внести доменные события в проект.Концепция описана в посте Уди Даана - http://www.udidahan.com/2009/06/14/domain-events-salvation/

Вот код события домена

public interface IDomainEvent { }

public interface IHandleDomainEvents<T> where T : IDomainEvent
{
     void Handle(T args); 
}

public interface IEventDispatcher
{
    void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent;
}

public static class DomainEvents
{
    public static IEventDispatcher Dispatcher { get; set; }

    public static void Raise<TEvent>(TEvent eventToRaise) where TEvent : IDomainEvent
    {
        Dispatcher.Dispatch(eventToRaise);
    }
}

Самая важная часть - это реализация IEventDispatcher, которая отделяет событие домена от того, что должно произойтикогда событие поднято.Хитрость заключается в том, чтобы соединить эту муфту через контейнер.Вот моя попытка

кода для регистрации всех обработчиков событий домена ....

        var asm = Assembly.GetExecutingAssembly();
        var handlerType = typeof(IHandleDomainEvents<>);

        builder.RegisterAssemblyTypes(asm)
            .Where(t => handlerType.IsAssignableFrom(t)
                        && t.IsClass
                        && !t.IsAbstract)
            .AsClosedTypesOf(handlerType)
            .InstancePerLifetimeScope(); 

и разрешения всех обработчиков событий в диспетчере.Проблема в том, что обработчики не решены.

public class EventDispatcher : IEventDispatcher
{
    private readonly IContainer _container;

    public EventDispatcher(IContainer container)
    {
        _container = container;
    }

    public void Dispatch<TEvent>(TEvent eventToDispatch) where TEvent : IDomainEvent
    {
        var handlers = _container.Resolve<IEnumerable<IHandleDomainEvents<TEvent>>>().ToList();
        handlers.ForEach(handler => handler.Handle(eventToDispatch));
    }
}

Поэтому я неправильно регистрирую обработчики событий или не разрешаю их.Как проверить, что регистрация работает?

Пример кода обработчика

public class SendWebQuestionToCSO : IHandleDomainEvents<JobNoteCreated>
{
    private readonly IConfig _config;

    public SendWebQuestionToCSO(IConfig config)
    {
        _config = config;
    } 

    public void Handle(JobNoteCreated args)
    {
        var jobNote = args.JobNote;
        using(var message = new MailMessage())
        {
            var client = new SmtpClient {Host = _config.SmtpHostIp};
            message.From = new MailAddress(_config.WhenSendingEmailFromWebProcessUseThisAddress);
            ...... etc
        }
    }
}

ОБНОВЛЕНИЕ После некоторых проб и ошибок EventDispatcher работает нормально!Если я вручную регистрирую обработчик и затем запускаю событие домена, это работает.Сборка сканирования / регистрации - моя проблема.Код регистрации вручную ...

builder.RegisterType<SendWebQuestionToCSO >().As<IHandleDomainEvents<JobNoteCreated>>();

Так как мне отсканировать все сборки для всех IHandleDomainEvents<>, если они выглядят так

public class SendWebQuestionToCSO : IHandleDomainEvents<JobNoteCreated>

Ответы [ 3 ]

8 голосов
/ 13 апреля 2011

Как указал Питер, проблема с регистрацией была в вашем предложении Where().

При сканировании сборок Autofac автоматически фильтрует компоненты на основе указанных вами сервисов, поэтому было бы одинаково правильно просто использовать:

builder.RegisterAssemblyTypes(asm)
    .AsClosedTypesOf(handlerType)
    .InstancePerLifetimeScope();
1 голос
/ 13 апреля 2011

Проблема в коде сканирования вашей сборки заключается в том, что вы используете IsAssignableFrom. Фильтр спросит: «Может ли экземпляр SendWebQuestionToCSO быть назначен переменной IHandleDomainEvents<>?» Ответ, очевидно, «нет», поскольку вы никогда не можете иметь переменную открытого универсального типа.

Хитрость заключается в том, чтобы проверять интерфейсы, реализованные каждым типом, и проверять, закрывают ли какие-либо из них открытый тип общего интерфейса. Вот пересмотренный сканер:

    var asm = Assembly.GetExecutingAssembly();
    var handlerType = typeof(IHandleDomainEvents<>);

    builder.RegisterAssemblyTypes(asm)
        .Where(t => t.GetInterfaces().Any(t => t.IsClosedTypeOf(handlerType)))
        .AsImplementedInterfaces()
        .InstancePerLifetimeScope(); 
0 голосов
/ 12 апреля 2011

Я не знаком с Autofac, но я думаю, что у меня похожая проблема, не с autofac, а с архитектурой. Динамическое приведение универсального интерфейса .

.

Это, вероятно, проблема Ковариация и Контравариантность .

...