EvnetBus
领域事件(EvnetBus)
本节内容:
在C#里,一个类可以定义自己的事件,然后其它类可以注册它,当某些事情发生时,接收到通知。这对于桌面应用或单机的Windows服务非常有用。但是,对于一个Web应用,它就有点问题,因为对象在一个web请求里创建,并且它们生命周期都很短。所以就难于注册一些类事件,同时,直接注册另一个类的事件,也使得类之间更加藕合。
领域事件一般用来解藕业务逻辑和在应用里发生重要领域修改时发出通知。
EventBus
EventBus是一个单例对象,被所有类触发事件或处理事件时共享。为使用事件总线,你先要引用它,有两种方式。
注入 IEventBus
你可以用依赖注入获取一个IEventBus的引用,这儿我们使用属性注入模式:

public class TaskAppService : ApplicationService
{
; }
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自动记录了所有异常)。你可以注册这个事件,当异常发生时,发出通知。
实体修改
为实体修改提供了泛型的事件:EntityCreationEventData<Tentity>、EntityCreatedEventData<TEntity>、EntityUpdatingEventData<TEntity>、EntityUpdateEventData<TEntity>、EntityDeletingEventData<TEntity>和EntityDeletedEventData<TEntity>,同样也有EntityChangingEventData<TEntity>和EntityChangedEventData<TEntity>,修改可以是插入、更新或删除。
“ing”事件(例如EntityUpdating)在保存修改(SaveChanges)前触发,所以你可以在这些事件里,通过抛出异常,促使工作单元回滚,阻止操作)。“ed”事件(例如EntityUpdated)在保存修改之后被触发,也就没有机会让工作单元回滚了。
实体修改事件定义在Abp.Events.Bus.Entities命名空间里,并在插入、更新或删除实体时,被ABP自动触发。如果你有一个Person实体,你可以注册EntityCreatedEventData<Person>,当一个新的Person创建并插入到数据库后,就可以收到通知。这些事件也支持继承,如果你有一个继承自Person的Student类,并且注册了EntityCreatedEventData<Person>,当一个Person或Student被插入后,你也会收到通知。
触发事件
触发一个事件很简单:

public class TaskAppService : ApplicationService
{
; } public TaskAppService()
{
EventBus = NullEventBus.Instance;
} public void CompleteTask(CompleteTaskInput input)
{
//TODO: complete the task on database...
EventBus.Trigger(new TaskCompletedEventData {TaskId = 42});
}
}

Trigger方法有几个重载:
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集合(查看实体文档的相关小节)。
处理事件
为处理一个事件,你应该实现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>来处理应用中的所有事件,你可能不想这样做,但它是可以做到的。
处理程序异常
在处理程序(Handler)抛出一个/一些异常时,Eventbus触发所有Handler事件,如果只有一个处理程序抛出异常,异常会直接被Trigger方法抛出,如果多个处理程序抛出异常,EventBus只为它们抛出一个AggregateException异常。
处理多个事件
在一个处理程序里你可以处理多个事件,此次,你应该为每个事件实现IEventHandler<T>,例如:

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

处理程序注册
为处理事件,我们必须在事件总线里注册处理程序。
自动
ABP找到所有实现IEVentHandler的类并注册到依赖注入(例如:通过实现ITransientDependency,如上面的示例),然后ABP自动把它们注册到事件总线,当一个事件发生,ABP使用依赖注入得到处理程序的引用,并在事件处理后释放该引用。在ABP里,这是使用事件总线的推荐的方式。
手动
可以手动注册事件,但要小心使用。在一个web应用里,事件注册应当中应用启动里完成。在一个Web请求里,注册事件不是一个好的方式,因为注册类请完成后继续注册,并为每个请求重新注册,这可能会引起问题,因为注册类多次被调用。同时要记住,手动注册不使用依赖注入系统。
事件总线的Register方法有几个重载,最简单的是接受一个委托(或lambda):
EventBus.Register<TaskCompletedEventData>(eventData =>
{
WriteActivity("A task is completed by id = " + eventData.TaskId);
});
“任务完成”事件发生后,这个lambda方法就会被调用。第二个是接受一个实现了IEventHantler<T>的对象:
EventBus.Register<TaskCompletedEventData>(new ActivityWriter());
同样是为事件调用ActivityWriter实例。第三个重载接受两个泛型参数:
EventBus.Register<TaskCompletedEventData, ActivityWriter>();
此次,事件总线为每个事件创建一个新的ActivityWriter,如果它是disposable(可释放),并调用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方法返回的值,例如:

//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也提供了Unregister方法,使用示例:

//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()方法反注册所有事件的所有处理程序。
EvnetBus的更多相关文章
- Android之EventBus使用详解
一.概述 当Android项目越来越庞大的时候,应用的各个部件之间的通信变得越来越复杂,例如:当某一条件发生时,应用中有几个部件对这个消息感兴趣,那么我们通常采用的就是观察者模式,使用观察者模式有一个 ...
- Android -- EventBus解析
EventBus EventBus 是一个 Android 事件发布/订阅框架,通过解耦发布者和订阅者简化 Android 事件传递.传统的事件传递方式包括:Handler.BroadCastRece ...
- Fragment使用具体解释
fragment是Google在3.0版本号中推出的新功能,如今已经增加到V4包中,假设要使用V4兼容包中的Fragment须要将Activity换成FragmentActivity,调用的getSu ...
随机推荐
- [改善Java代码]减少HashMap中元素的数量
在系统开发中我们经常会使用HashMap作为数据集容器,或者是用缓冲池来处理,一般很稳定,但偶尔也会出现内存溢出的问题(OutOfMemory错误),而且这经常是与HashMap有关的.而且这经常是与 ...
- [改善Java代码]性能考虑,数组是首选
建议60:性能考虑,数组是首选 一.分析 数组在实际的系统开发中使用的越来越少,我们通常只有在阅读一些开源项目时才会看到它们的身影,在Java中它确实没有List.Set.Map这些集合使用起来方便 ...
- Linux 命令 - ftp: 网络文件传输工具
命令格式 ftp [-pinegvd] [host] 命令参数 -A 传输文件模式为主动模式. -p 传输文件模式为被动模式. -i 关闭交互模式. -n 关闭自动登录功能. -e 不记录历史命令. ...
- C#算法基础之选择排序
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- 在多个linux服务器上执行一个命令
把服务器的ip地址写到list.txt中 192.168.37.3 192.168.37.4 192.168.37.6 然后运行 for i in `cat list.txt`;do ssh user ...
- sqlsever 关于索引
索引: 在sqlserver中,存储的单位最小是页,页是不可再分的B树:初衷是减少对磁盘的扫描次数,如果一个表或者索引没有使用B树(对于没有聚集索引的表是使用 Heap 堆进行存储的),那么查找一个数 ...
- mysql绿色版在windows系统中的启动
mysql绿色版在windows系统中的启动 1.下载mysql免安装版 例如:mysql-5.7.11-winx64 2.修改配置文件,my-default.ini名称改为:my.ini,文件里面的 ...
- spring mvc 拦截器
拦截器作用:可以用于用户操作的安全检查,如:登录.权限等 package com.tool; import java.util.List; import javax.servlet.http.Http ...
- C#中Predicate<T>与Func<T, bool>泛型委托的用法实例
本文以实例形式分析了C#中Predicate<T>与Func<T, bool>泛型委托的用法,分享给大家供大家参考之用.具体如下: 先来看看下面的例子: 1 2 3 4 5 6 ...
- 在ASP.NET中实现压缩多个文件为.zip文件,实现批量下载功能 (转载并优化处理篇)
转自:http://blog.csdn.net/yanlele424/article/details/6895986 这段时间一直在做一个网站,其中遇到了一个问题,就是在服务器端压缩多个服务器端的文件 ...