使用RegisterInitializer来连接事件处理程序

本文关键字:事件处理 程序 连接 RegisterInitializer 使用 | 更新日期: 2023-09-27 17:49:51

我有一个使用简单注入器进行依赖注入的WCF服务。我想在容器引导程序中连接一些事件处理程序。我已经创建了一个接口IStatusChangeNotification:

public interface IStatusChangeNotification
{
    event EventHandler<int> JobStatusChange;
}

我的CommandHandler实现IStatusChangeNotification,有两个事件处理程序类EmailNotificationMmrNotification,每个定义一个Notify()方法。然后在我的引导代码中,我有以下内容:

container.Register<EmailNotification>();
container.Register<MmrNotification>();
container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>),
                                     Assembly.GetExecutingAssembly());
container.RegisterInitializer<IStatusChangeNotification>(scn => 
    {
        scn.JobStatusChange += container.GetInstance<EmailNotification>().Notify;
        scn.JobStatusChange += container.GetInstance<MmrNotification>().Notify;
    });

这有效并且通知被接收。我的问题是,这是否是连接事件处理程序的正确/最佳方法?我如何在请求结束时删除处理程序,并且无法删除它们会导致内存泄漏?

使用RegisterInitializer来连接事件处理程序

虽然您的方法可能有效,但我认为系统设计的这一部分可能值得与命令处理程序一样的关注。命令处理程序触发事件的最常见原因是发布描述某些业务相关操作的事件。因此,与其使用。net事件,不如像建模命令那样建模域事件:

// Abstractions
public interface IEventHandler<TEvent> where TEvent : IDomainEvent {
    void Handle(TEvent e);
}
public interface IEventPublisher {
    void Publish<TEvent>(TEvent e) where TEvent : IDomainEvent;
}
// Events
public class JobStatusChanged : IDomainEvent {
    public readonly int JobId;
    public JobStatusChanged(int jobId) {
        this.JobId = jobId;
    }
}
// Container-specific Event Publisher implementation
public class SimpleInjectorEventPublisher : IEventPublisher {
    private readonly Container container;
    public SimpleInjectorEventPublisher(Container container) {
        this.container = container;
    }
    public void Publish<TEvent>(TEvent e) {
        var handlers = container.GetAllInstances<IEventHandler<TEvent>>();
        foreach (var handler in handlers) {
            hanlder.Handle(e);
        }
    }
}

使用前面的基础结构,可以创建以下事件和命令处理程序:

// Event Handlers
public class EmailNotificationJobStatusChangedHandler
    : IEventHandler<JobStatusChanged> {
    public void Handle(JobStatusChanged e)  {
        // TODO: Implementation
    }
}
public class MmrNotificationJobStatusChangedHandler
    : IEventHandler<JobStatusChanged> {
    public void Handle(JobStatusChanged e)  {
        // TODO: Implementation
    }
}
// Command Handler that publishes 
public class ChangeJobStatusCommandHandler : ICommandHandler<ChangeJobStatus> {
    private readonly IEventPublisher publisher;
    public ChangeJobStatusCommandHandler(IEventPublisher publisher) {
        this.publisher = publisher;
    }
    public void Handle(ChangeJobStatus command) {
        // change job status
        this.publisher.Publish(new JobStatusChanged(command.JobId));
    }
}

现在你可以注册你的命令处理程序和事件处理程序如下:

container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>),
    Assembly.GetExecutingAssembly());
// This registers a collection of eventhandlers with RegisterAll,
// since there can be multiple implementations for the same event.
container.RegisterManyForOpenGeneric(typeof(IEventHandler<>),
    container.RegisterAll,
    Assembly.GetExecutingAssembly());

这消除了单独注册每个事件处理程序类的需要,因为它们只是IEventHandler<JobStatusChanged>的简单实现,并且都可以在一行代码中批量注册。也不需要使用RegisterInitializer使用自定义接口来挂钩任何事件。

这样做的其他优点是:

  • 命令处理程序和IEventPublisher接口之间的依赖关系使得该命令非常清楚地发布事件。
  • 这个设计更具可扩展性,因为当新的命令和事件被添加到系统中时,它不太可能改变组合根。
  • 它对你的域很有好处,因为每个事件在系统中都有自己的实体。
  • 改变事件处理的方式会容易得多,因为这是SimpleInjectorEventProcessor的实现细节。例如,您可以决定并行运行它们,在它们自己的事务中运行它们,稍后处理它们(通过将它们存储在事件存储中)。