点这里进入ABP系列文章总目录html
基于DDD的现代ASP.NET开发框架--ABP系列之1四、ABP领域层——领域事件(Domain events)
git
ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。github
ABP的官方网站:http://www.aspnetboilerplate.com数据库
ABP在Github上的开源项目:https://github.com/aspnetboilerplate架构
在C#中,一个类能够定义其专属的事件而且其它类能够注册该事件并监听,当事件被触发时能够得到事件通知。这对于对于桌面应用程序或独立的Windows Service来讲很是有用。可是, 对于Web应用程序来讲会有点问题,由于对象是根据请求(request)被建立而且它们的生命周期都很短暂。咱们很难注册其它类别的事件。一样地,直接注册其它类别的事件也形成了类之间的耦合性。框架
在应用系统中,领域事件被用于解耦而且重用(re-use)商业逻辑。单元测试
事件总线为一个单体(singleton)的对象,它由全部其它类所共享,可经过它触发和处理事件。要使用这个事件总线,你须要引用它。你能够用两种方式来实现:测试
你能够直接使用EventBus.Default。它是全局事件总线而且能够以下方式使用:网站
EventBus.Default.Trigger(...); //触发事件
除了直接使用EventBus.Default外,你还可使用依赖注入(DI)的方式来取得IEventBus的参考。这利于进行单元测试。在这里,咱们使用属性注入的范式:this
public class TaskAppService : ApplicaService { public IEventBus EventBus { get; set; } public TaskAppService() { EventBus = NullEventBus.Instance; } }
注入事件总线,采用属性注入比建构子注入更适合。事件是由类所描述而且该事件对象继承自EventData。假设咱们想要触发某个事件于某个任务完成后:
public class TaskCompletedEventData : EventData { public int TaskId { get; set; } }
这个类所包含的属性都是类在处理事件时所须要的。EventData类定义了EventSource(那个对象触发了这个事件)和EventTime(什么时候触发)属性。
ABP定义AbpHandledExceptionData事件而且在异常发生的时候自动地触发这个事件。这在你想要取得更多关于异常的信息时特别有用(即使ABP已自动地纪录全部的异常)。你能够注册这个事件而且设定它的触发时机是在异常发生的时候。
ABP也提供在实体变动方面许多的通用事件数据类: EntityCreatedEventData, EntityUpdatedEventData和EntityDeletedEventData。它们被定义在Abp.Events.Bus.Entitis命名空间中。当某个实体新增/更新/删除后,这些事件会由ABP自动地触发。若是你有一个Person实体,能够注册到EntityCreatedEventData,事件会在新的Person实体建立且插入到数据库后被触发。这些事件也支持继承。若是Student类继承自Person类,而且你注册到EntityCreatedEventData中,接着你将会在Person或Student新增后收到触发。
触发事件的范例以下:
public class TaskAppService : ApplicationService { public IEventBus EventBus { get; set; } public TaskAppService() { EventBus = NullEventBus.Instance; } public void CompleteTask(CompleteTaskInput input) { //TODO: 已完成数据库上的任务 EventBus.Trigger(new TaskCompletedEventData { TaskId = 42 } ); } }
这里有一些触发方法的重载:
EventBus.Trigger<TaskcompletedEventData>(new TaskCompletedEventData { TaskId = 42}); EventBus.Trigger(this, new TaskCompletedEventData { TaskId = 42 }); EventBus.Trigger(typeof(TaskCompletedEventData), this, new TaskCompletedEventData { TaskId = 42});
要进行事件的处理,你应该要实现IEventHandler接口以下所示:
public class ActivityWriter : IEventHandler<TaskCompletedEventData>, ITransientDependency { public void HandleEvent(TaskCompletedEventData eventData) { WriteActivity("A task is completed by id = " + eventData.TaskId); } }
EventBus已集成到依赖注入系统中。就如同咱们在上例中实现ITransientDependency那样,当TaskCompleted事件触发,它会建立一个新的ActivityWriter类的实体而且调用它的HandleEvent方法,并接着释放它。详情请见依赖注入(DI)一文。
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来处理这两个事件:
public class ActivityWriter : IEventHandler<TaskEventData>, ITransientDependency { public void HandleEvent(TaskEventData eventData) { if(eventData is TaskCreatedEventData) { ... }else{ ... } } }
固然,你也能够实现IEventHandler来处理全部的事件,若是你真的想要这样作的话(译者注:做者不太建议这种方式)。
在单个处理器(handler)中咱们能够能够处理多个事件。此时,你应该针对不一样事件实现IEventHandler。范例以下:
public class ActivityWriter : IEventHandler<TaskCompletedEventData>, IEventHandler<TaskCreatedEventData>, ITransientDependency { public void HandleEvent(TaskCompletedEventData eventData) { //TODO: 处理事件 } public void HandleEvent(TaskCreatedEventData eventData) { //TODO: 处理事件 } }
咱们必需注册处理器(handler)到事件总线中来处理事件。
ABP扫描全部实现IEventHandler接口的类,而且自动注册它们到事件总线中。当事件发生, 它经过依赖注入(DI)来取得处理器(handler)的引用对象而且在事件处理完毕以后将其释放。这是比较建议的事件总线使用方式于ABP中。
也能够经过手动注册事件的方式,可是会有些问题。在Web应用程序中,事件的注册应该要在应用程序启动的时候。当一个Web请求(request)抵达时进行事件的注册,而且反复这个行为。这可能会致使你的应用程序发生一些问题,由于注册的类能够被调用屡次。一样须要注意的是,手动注册没法与依赖注入系统一块儿使用。
ABP提供了多个事件总线注册方法的重载(overload)。最简单的一个重载方法是等待委派(delegate)或Lambda。
EventBus.Register<TaskCompletedEventData>(eventData => { WriteActivity("A task is completed by id = " + eventData.TaskId); });
所以,事件:task completed会发生,而这个Lambda方法会被调用。第二个重载方法等待的是一个对象,该对象实现了IEventHandler:
Eventbus.Register<TaskCompletedEventData>(new ActivityWriter());
相同的例子,若是ActivityWriter因事件而被调用。这个方法也有一个非泛型的重载。另外一个重载接受两个泛化的参数:
EventBus.Register<TaskCompletedEventData, ActivityWriter>();
此时,事件总线建立一个新的ActivityWriter于每一个事件。当它释放的时候,它会调用ActivityWriter.Dispose方法。
最后,你能够注册一个事件处理器工厂(event handler factory)来负责建立处理器。处理器工厂有两个方法: GetHandler和ReleaseHandler,范例以下:
public class ActivityWriterFactory : IEventHandlerFactory { public IEventHandler GetHandler() { return new ActivityWriter(); } public void ReleaseHandler(IEventHandler handler) { //TODO: 释放ActivityWriter实体(处理器) } }
ABP也提供了特殊的工厂类,IocHandlerFactory,经过依赖注入系统,IocHandlerFactory能够用来建立或者释放(dispose)处理器。ABP能够自动化注册IocHandlerFactory。所以,若是你想要使用依赖注入系统,请直接使用自动化注册的方式。
当你手动注册事件总线,你或许想要在以后取消注册。最简单的取消事件注册的方式即为registration.Dispose()。举例以下:
//注册一个事件 Var registration = EventBus.Register<TaskCompletedEventData>(eventData => WriteActivity("A task is completed by id = " + eventData.TaskId)); //取消注册一个事件 registration.Dispose();
固然,取消注册能够在任何地方任什么时候候进行。保存(keep)好注册的对象而且在你想要取消注册的时候释放(dispose)掉它。全部注册方法的重载(overload)都会返回一个可释放(disposable)的对象来取消事件的注册。
事件总线也提供取消注册方法。使用范例:
//建立一个处理器 var handler = new ActivityWriter(); //注册一个事件 EventBus.Register<TaskCompletedEventData>(handler); //取消这个事件的注册 EventBus.Unregister<TaskCompletedEventData>(handler);
它也提供重载的方法给取消注册的委派和工厂。取消注册处理器对象必须与以前注册的对象是同一个。
最后,EventBus提供一个UnregisterAll()方法来取消某个事件全部处理器的注册,而UnregisterAll()方法则是全部事件的全部处理器。
但愿更多国内的架构师能关注到ABP这个项目,也许这其中有能帮助到您的地方,也许有您的参与,这个项目能够发展得更好。
欢迎加ABP架构设计交流QQ群:134710707