使用RegisterInitializer绑定事件处理程序 [英] Using RegisterInitializer to wire event handlers

查看:153
本文介绍了使用RegisterInitializer绑定事件处理程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

  public interface IStatusChangeNotification 
{
事件EventHandler< int> JobStatusChange;
}

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

  container.Register< EmailNotification>(); 
container.Register< MmrNotification>();

container.RegisterManyForOpenGeneric(typeof(ICommandHandler<)),
Assembly.GetExecutingAssembly());

container.RegisterInitializer< IStatusChangeNotification>(scn =>
{
scn.JobStatusChange + = container.GetInstance< EmailNotification>()。通知;
scn。 JobStatusChange + = container.GetInstance< MmrNotification>()。Notify;
});

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

解决方案

尽管您的方法可能会起作用,我认为您的系统设计的这一部分可能会受到与您的命令处理程序相同的关注。命令处理程序触发事件的最常见原因是发布描述一些业务相关操作的事件。所以不是使用.NET事件,而是模拟这些域事件,就像模拟你的命令一样:

  // Abstractions 
public interface IEventHandler< TEvent>其中TEvent:IDomainEvent {
void Handle(TEvent e);
}

public interface IEventPublisher {
void Publish< TEvent>(TEvent e)其中TEvent:IDomainEvent;
}

//事件
public class JobStatusChanged:IDomainEvent {
public readonly int JobId;
public JobStatusChanged(int jobId){
this.JobId = jobId;
}
}

//容器特定的事件发布者实现
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){
hanlder.Handle(e);
}
}
}

可以创建以下事件和命令处理程序:

  //事件处理程序
public class EmailNotificationJobStatusChangedHandler
:IEventHandler< ; JobStatusChanged> {
public void Handle(JobStatusChanged e){
// TODO:Implementation
}
}

public class MmrNotificationJobStatusChangedHandler
:IEventHandler< JobStatusChanged> {
public void Handle(JobStatusChanged e){
// TODO:Implementation
}
}

//发布
的命令处理程序public class ChangeJobStatusCommandHandler:ICommandHandler< ChangeJobStatus> {
private readonly IEventPublisher publisher;
public ChangeJobStatusCommandHandler(IEventPublisher publisher){
this.publisher = publisher;
}

public void Handle(ChangeJobStatus command){
//更改作业状态
this.publisher.Publish(new JobStatusChanged(command.JobId));
}
}

现在,您可以将命令处理程序和事件处理程序注册为如下:

  container.RegisterManyForOpenGeneric(typeof(ICommandHandler ))
Assembly.GetExecutingAssembly());

//注册一个有RegisterAll,
//的事件处理程序集合,因为同一个事件可以有多个实现。
container.RegisterManyForOpenGeneric(typeof(IEventHandler<)),
container.RegisterAll,
Assembly.GetExecutingAssembly());

这不需要单独注册每个事件处理程序类,因为它们只是<$ c的实现$ c> IEventHandler< JobStatusChanged> ,都可以通过一行代码批量注册。还没有必要使用 RegisterInitializer 使用自定义界面来钩住任何事件。



其他优点是:




  • 命令处理程序和 IEventPublisher 接口之间的依赖关系使得非常清楚这个命令是发布事件。

  • 设计更加可扩展,因为组合根不太可能在将新的命令和事件添加到系统时改变。 >
  • 它的域很好,因为每个事件都在系统中获得自己的实体。

  • 更改事件处理方式会更容易,因为现在是 SimpleInjectorEventProcessor 的实现细节。例如,您可以同时运行它们,在自己的事务中运行它们,稍后处理(通过将其存储在事件存储中)。


I have a WCF service that uses Simple Injector for dependency injection. I want to wire up some event handlers in the container bootstrapper. I have created an interface IStatusChangeNotification:

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

My CommandHandler implements IStatusChangeNotification and there are two event handler classes EmailNotification and MmrNotification, each defining a Notify() method. Then in my bootstrap code I have the following:

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

This works and the notifications are received. My question is whether this is the correct/best approach for wiring up event handlers? How do I remove the handlers at the end of the request and will failing to remove them result in a memory leak?

解决方案

Although your approach might work, I think this part of your system design might deserve the same amount of attention as your command handlers do. The most common reason for command handlers to trigger events, is to publishing events that describe some business related action. So instead of using .NET events, model those domain events the same way as you model your commands:

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

With the previous infrastructure, you can create the following event and command handlers:

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

Now you can register your command handlers and event handlers as follows:

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

This removes the need to register each event handler class seperately, since they are simply implementations of IEventHandler<JobStatusChanged> and can all be batch-registered in one line of code. There's also no need to use RegisterInitializer to hook any events using custom defined interfaces.

Other advantages of this are:

  • The dependency between a command handler and the IEventPublisher interface makes it very clear that this command is publishing events.
  • The design is much more scalable, since its less likely for the composition root to have to change when new commands and events are added to the system.
  • It does your domain much good, since each event gets its own entity in the system.
  • It will be much easier to change the way events are processed, since that's now an implementation detail of the SimpleInjectorEventProcessor. For instance, you can deside to run them in parallel, run them in their own transaction, process them later (by storing them in an event store).

这篇关于使用RegisterInitializer绑定事件处理程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆