In C#, a class can define own events and other classes can register it to be notified when something happen. This is useful for a desktop application or standalone windows service. But, for a web application it's a bit problematic since objects are created in a web request and they are short-lived. It's hard to register some class's events. Also, directly registering to another class's event makes classes tightly coupled.

在C #里,类可以定义自己的事件和其他类可以登记时要通知的事情发生。这对于桌面应用程序或独立的Windows服务非常有用。但是,对于Web应用程序来说,它有点问题,因为对象是在Web请求中创建的,它们是短命的。很难注册某些类的事件。另外,直接注册到另一个类的事件会使类紧密耦合。

Domain events can be used to decouple business logic and react to important domain changes in an application.

域事件可用于解耦业务逻辑并响应应用程序中的重要域更改。

EventBus

EventBus is a singleton object that is shared by all other classes to trigger and handle events. To use the event bus, you should get a reference to it. You can do it in two way.

eventbus是一个单例对象的所有其他类共享触发和事件处理。要使用事件总线,您应该得到它的引用。你可以用两种方式。

Injecting IEventBus

You can use dependency injection to get a reference to IEventBus. Here, we used property injection pattern:

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

Property injection is more proper than constructor injection to inject the event bus. Thus, your class can work without event bus. NullEventBus implements null object pattern. When you call it's methods, it does nothing at all.

属性注入比注入注入事件总线更合适。因此,您的类可以在没有事件总线的情况下工作。nulleventbus实现空对象模式。当你调用它的方法时,它什么也不做。

Getting The Default Instance

If you can not inject it, you can directly use EventBus.Default. It's the global event bus and can be used as shown below:

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

It's not suggested to directly use EventBus.Default wherever possible since it makes unit testing harder.

Defining Events

Before trigger an event, you should define the event first. An event is represented by a class that is derived from EventData. Assume that we want to trigger an event when a task is completed:

在触发事件之前,您应该首先定义事件。一个事件是由一个是来自事件数据类来表示。假设我们希望在任务完成时触发事件:

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

This class contains properties those are needed by class that handles the event. EventData class defines EventSource (which object triggered the event) and EventTime (when it's triggered) properties.

该类包含处理事件所需的类所需的属性。事件数据类定义(EventSource对象触发事件)和EventTime(当它触发)属性。

Predefined Events(预定义事件)

Handled Exceptions(处理异常)

ASP.NET Boilerplate defines AbpHandledExceptionData and triggers this event when it automatically handles any exception. This is especially useful if you want to get more information about exceptions (even ASP.NET Boilerplate automatically logs all exceptions). You can register to this event to be informed when an exception occurs.

ASP.NET的模板定义abphandledexceptiondata和触发此事件时,自动处理任何异常。如果你想获得有关异常的更多信息这是特别有用的(甚至ASP.NET样板自动记录所有的例外)。当异常发生时,您可以注册此事件通知。

Entity Changes

There are also generic event data classes for entity changes: EntityCreatingEventData<TEntity>, EntityCreatedEventData<TEntity>, EntityUpdatingEventData<TEntity>, EntityUpdatedEventData<TEntity>, EntityDeletingEventData<TEntity> and EntityDeletedEventData<TEntity>. Also, there are EntityChangingEventData<TEntity> and EntityChangedEventData<TEntity>. A change can be insert, update or delete.

'ing' events (ex: EntityUpdating) are triggered before saving changes. So, you can rollback the unit of work to prevent the operation by throwing exceptions in these events. 'ed' events (ex: EntityUpdated) are triggered after saving changes and no chance to rollback the unit of work.

Entity change events are defined in Abp.Events.Bus.Entities namespace and are automatically triggered by ASP.NET Boilerplate when an entity is inserted, updated or deleted. If you have a Person entity, can register to EntityCreatedEventData<Person> to be informed when a new Person created and inserted to database. These events also supports inheritance. If Student class derived from Person class and you registered to EntityCreatedEventData<Person>, you will be informed when a Person or Student is inserted.

也有通用的事件数据类实体的变化:entitycreatingeventdata < tentity >,< tentity entitycreatedeventdata >,< tentity entityupdatingeventdata >,< tentity entityupdatedeventdata >,< >和< entitydeletingeventdata tentity entitydeletedeventdata tentity >。另外,有entitychangingeventdata < >和< > tentity entitychangedeventdata tentity。可以插入、更新或删除更改。

ing的事件(例如:entityupdating)之前保存更改触发。因此,您可以通过在这些事件中抛出异常来回滚工作单元以防止操作。ED发生的事件(例如:entityupdated)被触发后保存更改并没有机会回滚工作单元。

实体变化的事件是在abp.events.bus.entities命名空间中定义的,是由ASP.NET自动样板时触发实体插入、更新或删除。如果你有一个实体,可以登记entitycreatedeventdata <person>被通知时,一个新的人创建并插入到数据库。这些事件也支持继承。如果学生来自人类和你注册entitycreatedeventdata <person>,你会被告知当一个人或一个学生插。

Triggering Events

Triggering an event is simple:

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});
}
}

There are some overloads of the trigger method:

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)

Another way of triggering events can be using DomainEvents collection of AggregateRoot class (see related section in the Entity documentation).

Handling Events

To handle an event, you should implement IEventHandler<T> interface as shown below:

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

IEventHandler defines HandleEvent method and we implemented it as shown above.

EventBus is integrated to dependency injection system. As we implemented ITransientDependency above, when a TaskCompleted event occured, it creates a new instance of ActivityWriter class and calls it's HandleEvent method, then disposes it. See dependency injection for more.

ieventhandler定义handleEvent方法我们实现了如上图所示。

eventbus集成依赖注入系统。我们实现了itransientdependency以上,当taskcompleted事件发生,造成了activitywriter类的新实例并调用它的handleEvent方法,然后处理它。查看更多的依赖注入。

Handling Base Events

Eventbus supports inheritance of events. For example, you can create a TaskEventData and two derived classes: TaskCompletedEventData and TaskCreatedEventData:

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; }
}

Then you can implement IEventHandler<TaskEventData> to handle both of the events:

public class ActivityWriter : IEventHandler<TaskEventData>, ITransientDependency
{
public void HandleEvent(TaskEventData eventData)
{
if (eventData is TaskCreatedEventData)
{
//...
}
else if (eventData is TaskCompletedEventData)
{
//...
}
}
}

That also means you can implement IEventHandler<EventData> to handle all events in the application. You probably don't want that, but it's possible.

Handler Exceptions

EventBus triggers all handlers even one/some of them throws exception. If only one of them throws exception, then it's directly thrown by the Trigger method. If more than one handler throws exception, EventBus throws a single AggregateException for all of them.

eventbus触发所有处理程序甚至一个/一些人抛出异常。如果其中一个抛出异常,则由触发器方法直接抛出。如果有多个处理程序抛出异常,eventbus把他们所有的单aggregateexception。

Handling Multiple Events

You can handle multiple events in a single handler. In this time, you should implement IEventHandler<T> for each event. Example:

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...
}
}

Registration Of Handlers

We must register the handler to the event bus in order to handle events.

Automatically

ASP.NET Boilerplate finds all classes those implement IEventHandler and registered to dependency injection (for example, by implementing ITransientDependency as the samples above). Then it registers them to the eventbus automatically. When an event occures, it uses dependency injection to get a reference to the handler and releases the handler after handling the event. This is the suggested way of using event bus in ASP.NET Boilerplate.

ASP.NET样板找出所有的类实现IEventHandler和注册依赖注入(例如,通过实施ITransientDependency作为样本以上)。然后,它记录了他们的eventbus自动。当一个事件发生时,它使用依赖注入来获得一个参考的处理和释放后的处理程序处理该事件。这是建议的方式使用事件总线在ASP.NET样板。

Manually(手动)

It is also possible to manually register to events but use it with caution. In a web application, event registration should be done at application start. It's not a good approach to register to an event in a web request since registered classes remain registered after request completion and re-registering for each request. This may cause problems for your application since registered class can be called multiple times. Also keep in mind that manual registration does not use dependency injection system.

也可以手动注册事件,但要谨慎使用。在Web应用程序中,事件注册应该在应用程序开始时完成。在Web请求中注册事件不是一个很好的方法,因为注册类在请求完成后仍然注册,并为每个请求重新注册。这可能会给应用程序带来问题,因为注册类可以多次调用。还要记住手动注册不使用依赖注入系统。

There are some overloads of register method of the event bus. The simplest one waits a delegate (or a lambda):

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

Thus, then a 'task completed' event occurs, this lambda method is called. Second one waits an object that implements IEventHandler<T>:

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

Same instance of ActivityWriter is called for events. This method has also a non-generic overload. Another overload accepts two generic arguments:

EventBus.Register<TaskCompletedEventData, ActivityWriter>();

In this time, event bus creates a new ActivityWriter for each event. It calls ActivityWriter.Dispose method if it's disposable.

Lastly, you can register an event handler factory to handle creation of handlers. A handler factory has two methods: GetHandler and ReleaseHandler. Example:

public class ActivityWriterFactory : IEventHandlerFactory
{
public IEventHandler GetHandler()
{
return new ActivityWriter();
} public void ReleaseHandler(IEventHandler handler)
{
//TODO: release/dispose the activity writer instance (handler)
}
}

There is also a special factory class, the IocHandlerFactory, that can be used to use dependency injection system to create/release handlers. ASP.NET Boilerplate also uses this class in automatic registration. So, if you want to use dependency injection system, directly use automatic registration defined before.

还有一个特殊的工厂类,这iochandlerfactory,可以使用依赖注入系统创建/释放句柄。ASP.NET也采用这类样板自动登记。所以,如果你想使用依赖注入系统,直接使用以前定义的自动注册。

Unregistration

When you manually register to event bus, you may want to unregister to the event later. Simplest way of unregistering an event is disposing the return value of the Register method. Example:

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

Surely, unregistration will be somewhere and sometime else. Keep registration object and dispose it when you want to unregister. All overloads of the Register method returns a disposable object to unregister to the event.

当然,注销将某时某地人。保持登记对象处理它当你想注销。所有重载的登记方法返回一个对象的一次性注销事件。

EventBus also provides Unregister method. Example usage:

//Create a handler
var handler = new ActivityWriter(); //Register to the event
EventBus.Register<TaskCompletedEventData>(handler); //Unregister from event
EventBus.Unregister<TaskCompletedEventData>(handler);

It also provides overloads to unregister delegates and factories. Unregistering handler object must be the same object which is registered before.

Lastly, EventBus provides a UnregisterAll<T>() method to unregister all handlers of an event and UnregisterAll() method to unregister all handlers of all events.

它还提供了过载注销代表和工厂。注销处理对象必须是相同的对象是注册前。

最后,eventbus提供unregisterall <T>()注销注销所有处理程序的所有处理程序的所有事件的事件和unregisterall()方法。

ABP框架系列之二十六:(EventBus-Domain-Events-领域事件)的更多相关文章

  1. ABP框架系列之二十:(Dependency-Injection-依赖注入)

    What is Dependency Injection If you already know Dependency Injection concept, Constructor and Prope ...

  2. ABP框架系列之二十八:(Handling-Exceptions-异常处理)

    Introduction This document is for ASP.NET MVC and Web API. If you're interested in ASP.NET Core, see ...

  3. ABP框架系列之二十四:(Email-Sending-EF-电子邮件发送)

    Introduction Email sending is a pretty common task for almost every application. ASP.NET Boilerplate ...

  4. ABP框架系列之二十二:(Dynamic-Web-API-动态WebApi)

    Building Dynamic Web API Controllers This document is for ASP.NET Web API. If you're interested in A ...

  5. ABP框架系列之二十五:(Embedded-Resource-Files-嵌入式资源文件)

    Introduction ASP.NET Boilerplate provides an easy way of using embedded Razor views (.cshtml files) ...

  6. ABP框架系列之二十九:(Hangfire-Integration-延迟集成)

    Introduction Hangfire is a compherensive background job manager. You can integrate ASP.NET Boilerpla ...

  7. ABP源码分析二十六:核心框架中的一些其他功能

    本文是ABP核心项目源码分析的最后一篇,介绍一些前面遗漏的功能 AbpSession AbpSession: 目前这个和CLR的Session没有什么直接的联系.当然可以自定义的去实现IAbpSess ...

  8. Spring Boot2 系列教程(二十六)Spring Boot 整合 Redis

    在 Redis 出现之前,我们的缓存框架各种各样,有了 Redis ,缓存方案基本上都统一了,关于 Redis,松哥之前有一个系列教程,尚不了解 Redis 的小伙伴可以参考这个教程: Redis 教 ...

  9. Unity 游戏框架搭建 2019 (二十六) 第一轮整理完结

    昨天呢我们把第八个示例整理完了.整理之后学习了类的第一作用:方法的集合,还有 Obselete 这个 API.并且在进行整理的时候贯彻了我们新的约定和规则:先确保功能有效,再去做变更和删除. 今天我们 ...

随机推荐

  1. Java如何运行一个class文件的main方法

    假设如下目录有个class文件,并且里面有main方法: d:\workspace\eclipse\com\JavaCore\classloader\MyClassLoader.class 首先进入c ...

  2. 长短记忆神经网络LSTM

    转载: https://www.jianshu.com/p/dcec3f07d3b5 https://blog.csdn.net/dream_catcher_10/article/details/48 ...

  3. 第24课 可变参数模板(5)_DllHelper和lambda链式调用

    1. dll帮助类 (1)dll的动态链接 ①传统的调用方式:先调用LoadLibrary来加载dll,再定义函数指针类型,接着调用GetProcAddress获取函数地址.然后通过函数指针调用函数, ...

  4. mybatis的动态sql编写以及一对一关系查询和一对多的查询

    创建mybatis数据库,运行以下sql语句 /* SQLyog Ultimate v8.32 MySQL - 5.5.27 : Database - mybatis **************** ...

  5. 浅析MySQL中concat以及group_concat的使用

      说明: 本文中使用的例子均在下面的数据库表tt2下执行: 一.concat()函数 1.功能:将多个字符串连接成一个字符串. 2.语法:concat(str1, str2,...) 返回结果为连接 ...

  6. TCP的三次握手与四次挥手理解及面试题(很全面)

    序列号seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数据字节都编上一个序号,第一个字节的编号由本地随机产生:给字节编上序号后,就给每一个报文段指派一个序号:序列号seq就是这个报文 ...

  7. Delphi中Chrome Chromium、Cef3学习笔记(二)

    原文   http://blog.csdn.net/xtfnpgy/article/details/46635739   用Tchromium替换webbrowser 用惯了EmbeddedWB,不想 ...

  8. mysql_config_editor usage

    # mysql_config_eidtor is a tool to create a profile file $HOME/.mylogin.cnf, in which you can store ...

  9. java应用健康检查

    本文主要针对自己手写shell监控应用状态,有可系统解决方案的,比如K8S,可以略过 #!/bin/sh#health_check.sh count=`ps -ef | grep test.jar | ...

  10. 2. Go变量(Variables)

    变量是什么,变量的命名规则,以及一些关于变量的基础没有必要再说了,我想学习Go语言的有很多都是从其他语言转过来的,那我们直接进入正题. 声明一个变量: var age int 给变量赋值: age = ...