ABP官方文档翻译 3.7 领域事件(事件总线)

领域事件(事件总线)

   在C#中,一个类可以定义自己的事件,其他类可以注册它以便当一些事情发生时就会被通知。对于桌面应用或者单独的windows服务而言,这是非常有用的。但是,对于Web应用,会有一点儿问题,因为对象在web请求中被创建并且是短暂存在的。注册类事件非常困难。直接注册另一个类的事件会使类变得紧耦合。

  在应用中,领域事件可以用来解耦业务逻辑且能够反应重要的领域更改。

事件总线

  事件总线是一个单例对象,被其他所有的类共享来触发和处理事件。为了使用事件总线,你需要获取一个它的引用。可以使用两种方式来获取。

注入IEventBus

  你可以使用依赖注入获取IEventBus的引用。这里,我们使用属性注入模式:

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

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

  属性注入比构造函数注入更加适合注入事件总线。因此,你的类没有事件总线也可以正常工作。NullEventBus实现了空对象模式。当你调用它的方法时,它什么也不做。

获取默认实例

  如果你不能注入它的话,你可以直接使用EventBus.Default。它是全局的时间总线且可以按如下所示使用:

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

  不建议直接使用EventBus.Default,因为它使单元测试变得困难。

定义事件

  在触发事件之前,你应该首先定义这个事件。事件以类的形式呈现,这个类集成自EventData。假定,当一个任务完成时我们想触发一个事件:

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

  这个类包含处理事件类所需要的属性。EventData类定义了EventSource(哪一个对象触发这个事件)和EventTime(什么时候触发)属性。

预定义事件

处理异常

  ABP定义了AbpHandledExceptionData,当ABP自动处理任何异常时会触发这个事件。当你想获取关于异常的更多信息时(甚至ABP自动记录所有的异常),这个会特别有用。你可以注册这个事件,当异常发生时便会被通知。

实体更改

  实体变更也有通用的事件数据类。EntityCreatingEventData<TEntity>、EntityCreatedEventData<TEntity>、EntityUpdatingEventData<TEntity>、EntityUpdatedEventData<TEntity>、ENtityDeletingEventData<TEntity>和EntityDeletedEventData<TEntity>。还有EntityChangingEventData<TEntity>和EntityChangedEventData<TEntity>。更改可以插入、更新和删除。

  ‘ing’事件(例如:EntityUpdating)在保存更改之前触发。所以你可以通过在这些事件中抛出异常来回滚工作单元以阻止操作。‘ed’事件(例如:EntityUpdated)在保存更改之后发生,这时就没有机会回滚工作单元了。

  实体更改事件在Abp.Events.Bus.Entities命名空间中定义,当一个实体被插入、更新或删除时,ABP自动触发。如果你有一个Person实体,可以注册到EntityCreatedEvnetData<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 on 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)

  另一种触发事件的方式是使用聚合根的领域事件集合。(在Entity documentation中查看相关部分)

 处理事件

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

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

  IEventHandler定义了HandleEvent方法,我们按如上所示实现它。

  事件总线集成到了依赖注入系统。如我们上面实现的ITransientDependency,当一个TaskCompleted事件发生时,它创建一个ActivityWriter类的新实例,并调用它的HandleEvent方法,然后释放他。参见依赖注入了解更多。

处理基础事件

  事件总线支持事件继承。例如,你创建了一个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>处理应用中的所有事件。你或许不希望这样,但是这是可以实现的。

处理者异常

  事件总线触发所有的处理者,即使他们中的一个或一些抛出异常。如果他们中的一个抛出异常,然后他被触发方法直接抛出。如果多余一个处理者抛出异常,事件总线会抛出他们的一个聚合异常。

处理多个事件

  你可以在一个处理者中处理多个事件。这时,你应该为每一个事件实现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...
    }
}

注册处理者

  我们必须注册处理者到事件总线,以便能够处理事件。

自动

  ABP查找所有实现IEventHandler的类,并注册的依赖注入(例如,如上面示例所示的实现ITransientDependency)。然后,它自动注册他们到事件总线。当一个事件发生时,它使用依赖注入得到处理者的一个引用,并在处理事件之后释放处理者。在ABP中,推荐使用这种方式使用事件总线。

手动

  手动注册也是可以的,但需要小心。在web应用中,事件注册需要在应用开始时完成。在一个web请求中注册一个事件不是一个好方式,因为被注册的类在请求结束后仍然保持被注册状态,且为每一次请求重新注册。这会导致你的应用出现问题,因为注册类会被调用多次。还要记得,手动注册不使用依赖注入系统。

  这有一些事件总线注册方法的重载。最简单一个接收一个委托(或拉姆达表达式):

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

  因此当一个'task completed'事件发生时,会调用这个拉姆达表达式。第二个接收一个实现了IEventHandler<T>接口的对象:

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

  多个事件会调用同一个ActivityWriter实例。这个方法有一个非泛型的重载版本。另一个重载接收两个泛型参数:

EventBus.Register<TaskCompletedEventData, ActivityWriter>();

  这次,事件总线为每个事件创建一个ActivityWriter实例。如果它是一次性处理的,它将调用ActivityWriter.Dispose方法。

  最后,你可以注册一个事件处理者工厂来处理处理者的创建。一个处理者工厂有两个方法:GetHandler和Releasehandler。例如:

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

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

  这有一个特殊工厂类,IocHandlerFactory,可以用来使用依赖注入系统创建或释放处理者。ABP也使用这个类来实现自动注册。所以,如果你想使用依赖注入系统,直接使用之前定义的自动注册。

取消注册

  当你手动注册事件总线,你或许想稍后取消注册这个事件。取消注册事件的最简单方式是释放注册方法的返回值。示例:

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

//Unregister from event
registration.Dispose();

  当然,取消注册可能在其他地方或其他时间。保留注册对象并当你想取消注册时释放它。注册方法的所有重载都返回一个可释放对象用来取消注册事件。

  事件总线也提供了Unregister方法。示例用法:

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

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

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

  它也提供了重载来取消注册委托和工厂。取消注册处理者对象必须是之前注册的同一个对象。

  最后,事件总线提供了一个UnregisterAll<T>方法用来取消注册一个事件的所有处理者和UnregisterAll()方法用来取消所有事件的所有注册者。

 

返回主目录

 

posted @ 2017-05-18 23:25  Yung2022  阅读(1332)  评论(0编辑  收藏  举报