zoukankan      html  css  js  c++  java
  • ABP理论学习之事件总线和领域事件

    返回总目录


    本篇目录

    在C#中,我们可以在一个类中定义自己的事件,而其他的类可以注册该事件,当某些事情发生时,可以通知到该类。这对于桌面应用或者独立的windows服务来说是非常有用的。但对于一个web应用来说是有点问题的,因为对象都是在web请求中创建的,而且这些对象生命周期都很短,因而注册某些类的事件是很困难的。此外,注册其他类的事件会使得类紧耦合。

    领域事件用于解耦并重复利用应用中的逻辑。

    事件总线###

    事件总线是被所有触发并处理事件的其他类共享的单例对象。要使用事件总线,首先应该获得它的一个引用。下面有两种方法来处理:

    创建默认实例

    你可以直接使用 EventBus.Default。这是全局的事件总线,用法如下所示:

    EventBus.Default.Trigger(...); //触发一个事件
    
    

    注入IEventBus

    不直接使用EventBus.Default,你也可以使用依赖注入来获得IEventBus的引用。这有利于单元测试。这里我们使用属性注入模式:

    public class TaskAppService : ApplicationService
    {
        public IEventBus EventBus { get; set; }
            
        public TaskAppService()
        {
            EventBus = NullEventBus.Instance;
        }
    }
    
    

    对于注入事件总线这件事,属性注入比构造函数注入更合适。这样,你的类离开事件总线还能工作。NullEventBus实现了null对象模式。当你调用上面的构造函数时,实际上啥都没做。

    定义事件###

    触发事件之前,应该先要定义该事件。事件是使用派生自EventData的类来表示的。假设我们想当一个任务task完成时触发一个事件:

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

    该类包含了类处理事件需要的属性。EventData类定义了 EventSource(事件源)和 EventTime(事件触发时间)属性。

    预定义事件

    ABP定义了AbpHandleExceptionData,当自动处理任何异常时都会触发这个事件。如果你想要获得更多的关于异常的信息(甚至ABP会自动记录所有的异常),那么这是特别有用的。注册这个事件之后,异常发生时就会通知你。

    对于实体的更改也有泛型的事件数据类:EntityCreatedEventData,EntityUpdateEventDataEntityDeletedEventData。它们都定义在 Abp.Event.Bus.Entities命名空间中。当一个实体插入,更新或者删除时,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: 完成task的数据库操作...
            EventBus.Trigger(new TaskCompletedEventData {TaskId = 42});
        }
    }
    
    
    

    下面是Trigger方法的一些重载:

    EventBus.Trigger<TaskCompletedEventData>(new TaskCompletedEventData { TaskId = 42 }); //显示声明为泛型参数
    EventBus.Trigger(this, new TaskCompletedEventData { TaskId = 42 }); //将 '事件源'设置为'this'
    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)已经集成到ABP的依赖注入系统中。正如上面实现ITransientDependency一样,当TaskCompleted事件发生时,它会创建ActivityWriter类的一个新实例,然后调用HandleEvent方法,最后释放它。更多知识请查看依赖注入

    处理基事件

    事件总线支持事件的继承。比如,你创建了一个TaskEventData和它的两个子类: TaskCompletedEventDataTaskCreatedEventData:

    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 if (eventData is TaskCompletedEventData)
            {
                //...
            }
        }
    }
    
    
    

    当然了,你可以实现IEventHandler来处理所有你想要处理的事件。

    处理多事件

    在一个单一的处理句柄中,可以处理多个事件。这时,你应该为每个事件实现IEventHandler。比如:

    public class ActivityWriter : 
        IEventHandler<TaskCompletedEventData>, 
        IEventHandler<TaskCreatedEventData>, 
        ITransientDependency
    {
        public void HandleEvent(TaskCompletedEventData eventData)
        {
            //TODO: 处理事件...
        }
    
        public void HandleEvent(TaskCreatedEventData eventData)
        {
            //TODO: 处理事件...
        }
    }
    
    

    句柄注册###

    为了处理事件,我们必须将事件句柄注册给事件总线。

    自动

    ABP会自动扫描所有的实现了IEventHandler的类,并自动将它们注册到事件总线上。当一个事件发生时,它会使用依赖注入获得该句柄的一个引用,而且在处理该事件之后就会释放该句柄。建议这样使用ABP中的事件总线。

    手动

    也可能会手动注册到事件,但是要小心使用。在一个web应用中,事件注册应该在应用启动时完成。在web请求时注册到一个事件不是一个好的方法,因为请求完成之后注册的类仍旧是注册的,而且对于每个请求继续再次注册。这可能会对你的应用造成问题,因为注册的类可能被调用多次。而且要记住手动注册不会使用依赖注入系统。

    这里有一些事件总线的方法的重载。最简单的一个等待了一个委托(或者一个lambda):

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

    这样,当“一个task完成”事件发生时,这个lambda方法就会调用。第二个等待一个实现了IEventHandler的对象:

    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:释放ActivityWriter实例 (handler)
        }
    }
    
    

    还有一个特殊的工厂类IocHandlerFactory,它可以使用依赖注入系统创建或者释放句柄。ABP在自动注册模式中使用了这个类。因此,如果你想使用依赖注入系统,直接使用自动注册。

    取消注册###

    手动注册到事件总线时,你可能会在以后想要取消注册该事件。取消注册一个事件的最简单方法是释放该注册方法的返回值。如下所示:

    //注册到一个事件...
    var registration = EventBus.Register<TaskCompletedEventData>(eventData => WriteActivity("A task is completed by id = " + eventData.TaskId) );
    
    //取消注册事件
    registration.Dispose();
    
    

    当然了,注销注册会在某个地方和某个时间。保留注册对象并在想要取消注册时释放它。所有注册方法的重载都会返回一个可释放的对象以取消注册该事件。

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

    //创建一个句柄
    var handler = new ActivityWriter();
                
    //注册到事件
    EventBus.Register<TaskCompletedEventData>(handler);
    
    //从事件取消注册
    EventBus.Unregister<TaskCompletedEventData>(handler);
    
    

    它也提供了重载来注销委托和工厂,注销句柄对象必须是之前注册的相同对象。

    最后,事件总线提供了UnregisterAll方法来注销一个事件的所有句柄,RegisterAll()方法会注销所有事件的所有句柄。

  • 相关阅读:
    hashlib加密算法
    gc 模块常用函数
    functools函数中的partial函数及wraps函数
    ctime使用及datetime简单使用
    __new__方法理解
    __getattribute__小例子
    == 和 is 的区别
    线程_可能发生的问题
    线程_进程池
    【网站】 简单通用微信QQ跳转浏览器打开代码
  • 原文地址:https://www.cnblogs.com/farb/p/ABPEventBus.html
Copyright © 2011-2022 走看看