事件总线-领域事件

在C#中,类可以定义事件,其他类可以向它们注册,以便在发生事件时得到通知。这对于桌面应用程序或独立的Windows服务很有用,但对于Web应用程序来说,由于对象是在Web请求中创建的并且是短暂的,因此它有点问题。注册一些课堂活动很难。直接注册到另一个类的事件会使类紧密耦合。

域事件可用于解耦业务逻辑并对应用程序中的重要域更改做出反应。

EventBus

EventBus是一个单例对象,由其他类共享以触发和处理事件。要使用事件总线,您需要获取它的引用。你可以用两种方式做到这一点。

注入IEventBus

您可以使用依赖注入来获取对IEventBus的引用 在这里,我们使用了属性注入模式:

public class TaskAppService : ApplicationService
{
    public IEventBus EventBus { get; set; }

    public TaskAppService()
    {
        EventBus = NullEventBus.Instance;
    }
}

在注入事件总线时,属性注入比构造函数注入更合适。这样,您的班级可以在没有事件总线的情况下工作。NullEventBus实现了null对象模式当你调用它的方法时,它什么都不做。

获取默认实例

如果你不能注入它,你可以直接使用EventBus.Default它是全局事件总线,可以如下所示使用:

EventBus.Default.Trigger(...); //trigger an event

我们不建议您直接使用EventBus.Default,因为它会使单元测试更加困难。

定义事件

在触发事件之前,您需要先定义它。事件由从EventData派生的类表示假设我们想要在任务完成时触发事件:

public class TaskCompletedEventData : EventData
{
    public int TaskId { get; set; }
}

此类包含处理事件的类所需的属性。EVENTDATA类定义的EventSource(即触发事件的对象)和EVENTTIME(当它的触发)性能。

预定义的事件

处理异常

ASP.NET Boilerplate定义了AbpHandledExceptionData,并在自动处理异常时触发此事件。如果您想获得有关异常的更多信息(ASP.NET Boilerplate自动记录所有异常),这将特别有用。您可以注册此事件以在发生异常时通知您。

实体变更

还有用于实体更改的通用事件数据类: EntityCreatingEventData <TEntity>,EntityCreatedEventData <TEntity>, EntityUpdatingEventData <TEntity>,EntityUpdatedEventData <TEntity>,EntityDeletingEventData <TEntity>和 EntityDeletedEventData <TEntity>此外,还有EntityChangingEventData <TEntity>和 EntityChangedEventData <TEntity>更改可以是插入,更新或删除。

在提交事务之前触发'ing'事件(例如EntityUpdating)。这样,您可以回滚工作单元 并通过抛出异常来阻止操作。提交事务后会触发'ed'事件(例如EntityUpdated),为此您无法回滚工作单元。

实体更改事件在Abp.Events.Bus.Entities 命名空间中定义,并在插入,更新或删除实体时由ASP.NET Boilerplate 自动触发如果您有Person实体,则可以注册到EntityCreatedEventData <Person>,以便在创建新Person并将其插入数据库时通知。这些事件也支持继承。如果Student类派生自Person类,并且您注册到EntityCreatedEventData <Person>,则会在插入Person  Student 时通知您

触发事件

触发事件很简单:

public class TaskAppService : ApplicationService
{
    public IEventBus EventBus { get; set; }

    public TaskAppService()
    {
        EventBus = NullEventBus.Instance;
    }

    public void CompleteTask(CompleteTaskInput input)
    {
        //TODO: complete the task in the database...
        EventBus.Trigger(new TaskCompletedEventData {TaskId = 42});
    }
}

触发器方法有一些重载:

EventBus.Trigger<TaskCompletedEventData>(new TaskCompletedEventData { TaskId = 42 }); //Explicitly declare generic argument
EventBus.Trigger(this, new TaskCompletedEventData { TaskId = 42 }); //Set 'event source' as 'this'
EventBus.Trigger(typeof(TaskCompletedEventData), this, new TaskCompletedEventData { TaskId = 42 }); //Call non-generic version (first argument is the type of the event class)

触发事件的另一种方法是使用AggregateRoot类的DomainEvents集合(请参阅Entity文档中的相关部分)。

处理事件

要处理事件,您应该实现IEventHandler <T> 接口,如下所示:

public class ActivityWriter : IEventHandler<TaskCompletedEventData>, ITransientDependency
{
    public void HandleEvent(TaskCompletedEventData eventData)
    {
        WriteActivity("A task is completed by id = " + eventData.TaskId);
    }
}

IEventHandler定义了一个HandleEvent方法,并如上所示实现它。

EventBus集成到依赖注入系统中。我们实现了ITransientDependency(上面),所以当TaskCompleted事件发生时,它会创建一个ActivityWriter类的新实例,调用它的HandleEvent方法,然后处理它。有关详细信息,请参阅依赖注入

处理基本事件

Eventbus支持事件继承例如,您可以使用两个派生类创建TaskEventData基类:TaskCompletedEventData 和TaskCreatedEventData

public class TaskEventData : EventData
{
    public Task Task { get; set; }
}

public class TaskCreatedEventData : TaskEventData
{
    public User CreatorUser { get; set; }
}

public class TaskCompletedEventData : TaskEventData
{
    public User CompletorUser { get; set; }
}

然后,您可以实现IEventHandler <TaskEventData>来处理这两个事件:

public class ActivityWriter : IEventHandler<TaskEventData>, ITransientDependency
{
    public void HandleEvent(TaskEventData eventData)
    {
        if (eventData is TaskCreatedEventData)
        {
            //...
        }
        else if (eventData is TaskCompletedEventData)
        {
            //...
        }
    }
}

您可以实现IEventHandler <EventData>来处理应用程序中的所有事件。你可能不希望如此,但这是可能的。

异常处理程序

EventBus 触发所有处理程序,即使它们中的任何一个抛出异常。如果只有一个抛出异常,那么它会被Trigger方法直接抛出。如果多个处理程序抛出异常,则EventBus会为所有处理程序抛出一个AggregateException

处理多个事件

您可以在单个处理程序中处理多个事件如果是这样,您应该为每个事件实现IEventHandler <T>。例:

public class ActivityWriter :
    IEventHandler<TaskCompletedEventData>,
    IEventHandler<TaskCreatedEventData>,
    ITransientDependency
{
    public void HandleEvent(TaskCompletedEventData eventData)
    {
        //TODO: handle the event...
    }

    public void HandleEvent(TaskCreatedEventData eventData)
    {
        //TODO: handle the event...
    }
}

处理人员登记

我们必须在事件总线上注册处理程序才能处理事件。

自动

ASP.NET Boilerplate查找实现IEventHandler的所有类,这些类 通过依赖注入注册(例如,通过实现ITransientDependency作为上面的示例)。然后它会自动将它们注册到事件总线当事件发生时,它使用依赖注入来获取对处理程序的引用,并在处理事件后释放它。这是在ASP.NET Boilerplate中使用事件总线建议方法。

手动

也可以手动注册事件,但要谨慎使用!在Web应用程序中,事件注册应在应用程序开始时完成。注册Web请求中的事件并不是一个好方法,因为注册的类在请求完成后仍然会注册,并且会针对每个请求重新注册。这可能会导致您的应用程序出现问题,因为可以多次调用已注册的类。请记住,手动注册不使用依赖注入系统。

事件总线中的寄存器方法有一些重载。最简单的一个使用委托(或lambda):

EventBus.Register<TaskCompletedEventData>(eventData =>
    {
        WriteActivity("A task is completed by id = " + eventData.TaskId);
    });

当“任务完成”事件发生时,将调用此lambda方法。第二个等待实现IEventHandler <T>的对象:

EventBus.Register<TaskCompletedEventData>(new ActivityWriter());

为事件调用相同的ActivityWriter实例。此方法还具有非泛型重载。另一个重载接受两个泛型参数:

EventBus.Register<TaskCompletedEventData, ActivityWriter>();

在这种情况下,事件总线为每个事件创建一个新的ActivityWriter。然后,如果它是一次性的,它会调用ActivityWriter.Dispose方法 

最后,您可以注册一个事件处理程序工厂来处理创建处理程序。处理程序工厂有两个方法:GetHandlerReleaseHandler例:

public class ActivityWriterFactory : IEventHandlerFactory
{
    public IEventHandler GetHandler()
    {
        return new ActivityWriter();
    }

    public void ReleaseHandler(IEventHandler handler)
    {
        //TODO: release/dispose the activity writer instance (handler)
    }
}

还有一个特殊的工厂类IocHandlerFactory,可以与依赖注入系统一起使用来创建和释放处理程序。ASP.NET Boilerplate也在自动注册中使用此类,因此如果要使用依赖注入系统,请直接使用上面定义的自动注册。

注销

当你手工注册的事件总线,您可能需要 注销后的事件。取消注册事件的最简单方法是处理Register方法的返回值例:

//Register to an event...
var registration = EventBus.Register<TaskCompletedEventData>(eventData => WriteActivity("A task is completed by id = " + eventData.TaskId) );

//Unregister from event
registration.Dispose();

最有可能的是,取消注册将在其他地方以及稍后的时间。保留注册对象并在要取消注册时将其丢弃。Register方法的所有重载都返回一个一次性对象以注销该事件。

EventBus还提供取消注册方法。用法示例:

//Create a handler
var handler = new ActivityWriter();

//Register to the event
EventBus.Register<TaskCompletedEventData>(handler);

//Unregister from event
EventBus.Unregister<TaskCompletedEventData>(handler);

这也为取消注册委托和工厂提供了重载。取消注册处理程序对象必须在之前注册的同一对象上完成。

最后,EventBus提供了一个UnregisterAll <T>()方法来取消注册事件的所有处理程序,并提供UnregisterAll()方法来取消注册所有事件的所有处理程序。

nidie.com.cn - 用心与你沟通