领域事件(事件总线)

  • 事件总线

    • 注入IEventBus
    • 获取默认实例
  • 定义事件
    • 预定义事件

      • 处理异常
      • 实体更改
  • 触发事件
  • 处理事件
    • 处理基础事件
    • 处理者异常
    • 处理多个事件
  • 注册处理者
    • 自动
    • 手动
  • 取消注册

  在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()方法用来取消所有事件的所有注册者。

返回主目录

转载于:https://www.cnblogs.com/xajh/p/6875632.html

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

  1. ABP官方文档翻译 0.0 ABP官方文档翻译目录

    一直想学习ABP,但囿于工作比较忙,没有合适的契机,当然最重要的还是自己懒.不知不觉从毕业到参加工作七年了,没留下点儿什么,总感觉很遗憾,所以今天终于卯足劲鼓起勇气开始写博客.有些事能做的很好,但要跟 ...

  2. ABP官方文档翻译 3.1 实体

    实体 实体类 聚合根类 领域事件 常规接口 审计 软删除 激活/失活实体 实体改变事件 IEntity接口 实体是DDD(领域驱动设计)的核心概念之一.Eric Evans描述它为"An o ...

  3. ABP官方文档翻译 1.2 N层架构

    N层架构 介绍 ABP架构 其他(通用) 领域层 应用层 基础设施层 网络和展现层 其他 总结 介绍 应用程序代码库的分层架构是被广泛认可的可以减少程序复杂度.提高代码复用率的技术.为了实现分层架构, ...

  4. ABP官方文档翻译 6.1.3 异常处理

    处理异常 介绍 启用错误处理 Non-Ajax请求 显示异常 UserFriendlyException Error模型 AJAX请求 异常事件 介绍 此文档是与ASP.NET MVC和Web API ...

  5. ABP官方文档翻译 4.6 审计日志

    审计日志 介绍 关于IAuditingStore 配置 通过特性启用/禁用 注意事项 介绍 维基百科:"审计追踪(也称为审计日志)是与安全相关的按时间先后的记录.记录集合.记录的目的地和源, ...

  6. ABP官方文档翻译 6.3 本地化

    本地化 介绍 应用程序语言 本地化源 XML文件 注册XML本地化源 JSON文件 注册JSON本地化源 资源文件 自定义源 当前语言是如何决定的 ASP.NET Core ASP.NET MVC 5 ...

  7. ABP官方文档翻译 6.1.2 MVC视图

    ASP.NET MVC 视图 介绍 AbpWebViewPage基类 介绍 ABP通过Abp.Web.Mvc nuget包集成到MVC视图.你可以如往常一样创建正常的MVC视图. AbpWebView ...

  8. ABP官方文档翻译 9.2 Entity Framework Core

    Entity Framework Core 介绍 DbContext 配置 在Startup类中 在模块PreInitialize方法中 仓储 默认仓储 自定义仓储 应用程序特定基础仓储类 自定义仓储 ...

  9. ABP官方文档翻译 6.1.1 MVC控制器

    ASP.NET MVC控制器 介绍 AbpController基类 本地化 其他 过滤器 异常处理和结果包装 审计日志 验证 授权 工作单元 介绍 ABP通过Abp.Web.Mvc nuget包集成到 ...

最新文章

  1. 华为+长安研发芯片?长安蔚来更名“阿维塔科技”
  2. Visual studio 2005如何实现源码管理
  3. 第二十五课.元学习MetaLearning
  4. python官方手册-Python3 中文手册
  5. 用HTML制作一个漂亮的成绩表,JS-结合html综合练习js的对象——班级成绩表制作...
  6. Ookla speedtest网速测试算法实现
  7. MySql like 查询 变向写法(不用like 完成like查询)
  8. 它估值 25 亿!被马云领投,是华为“老战友”,网友:也许股价能超茅台!
  9. RPC框架设计概要-性能
  10. 论模式在领域驱动设计中的重要性
  11. [Cocos Creator] 制作简版消消乐(四):实现消除算法
  12. python Excel公式
  13. 针对ewebeditor编辑器漏洞一次实战经验
  14. “第二课堂”开课啦~
  15. 对圆柱面的曲面积分_积分曲面为圆柱面的曲面积分的计算
  16. ps、firewords在win78中无法直接拖入的问题解决方法
  17. Win自动配置VS Code的C++开发环境
  18. 终极孵化器:仿生婴儿的美丽新世界
  19. ConViT:使用软卷积归纳偏置改进视觉变换器
  20. 详解Android电量优化

热门文章

  1. arm linux输出到lcd,求助 armlinux中实现lcd显示
  2. HTML快速上手教程
  3. tar 打包、压缩和备份
  4. 国外软件测试方法,ATE软件测试方法研究及实现
  5. axure9数据统计插件_数据分析太棘手?常用8大统计软件解决难题!
  6. python histogram函数_python (ploit2)(histogram)
  7. 地磅称重软件源码_地磅称重软件需求梳理的重要性
  8. fread读取同一个文件得到缓冲区大小不同_c++日志文件操作
  9. PDE11 wave equation: d'Alembert examples
  10. 无人机图像深度学习的大豆害虫检测与分类