ABP文档笔记 - 事件BUS

时间:2022-09-14 10:18:41

文档:

EventBus(事件总线)

EventBus是一个单例对象,被所有类触发事件或处理事件时共享。

IEventBusConfiguration在应用启动时加载(by AbpCoreInstaller),依据它的配置决定 是所有IWindsorContainer实例共享同一条事件总线(EventBus.Default),还是每个IWindsorContainer实例创建一个自己的单例。默认使用EventBus.Default。

ABP文档笔记 - 事件BUS

因为在应用中IocManager使用单例,IIocManager.IocContainer也是单一的,所以事件总线使用的就是EventBus.Default。

注入 IEventBus

使用属性注入模式:

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

直接使用默认实例

如果你不能注入它,可以直接使用EventBus.Default。使用方式如下所示:

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

但就如同不推荐使用IocManager.Instance一样,在任何可能的地方都不建议直接使用EventBus.Default,因为它难于单元测试。

定义事件

事件定义,继承EventData

在触发一个事件前,你首先要定义它,通过一个继承自EventData的类来表现一个事件。

假设当一个任务完成后我们想触发一个事件:

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

这个类包含处理事件类所需要的属性,EventData类定义了EventSource(事件源,哪个对象触发了事件)和EventTime(何时触发的)属性。

ABP文档笔记 - 事件BUS

泛型的事件定义,使用IEventDataWithInheritableGenericArgument

public class EntityEventData<TEntity> : EventData , IEventDataWithInheritableGenericArgument
{
/// <summary>
/// Related entity with this event.
/// </summary>
public TEntity Entity { get; private set; } /// <summary>
/// Constructor.
/// </summary>
/// <param name="entity">Related entity with this event</param>
public EntityEventData(TEntity entity)
{
Entity = entity;
} public virtual object[] GetConstructorArgs()
{
return new object[] { Entity };
}
}

ABP框架预定义的事件

异常处理事件

命名空间Event.Bus.Exceptions中定义了ExceptionData 和 AbpHandledExceptionData,当ABP自动处理任何异常时,会触发后者。

实体修改事件

命名空间Abp.Events.Bus.Entities中为实体修改提供了泛型的事件:EntityCreationEventData、EntityCreatedEventData、EntityUpdatingEventData、EntityUpdateEventData、EntityDeletingEventData和EntityDeletedEventData,同样也有EntityChangingEventData和EntityChangedEventData。

“ing”事件(例如EntityUpdating)在保存修改(SaveChanges)前触发,所以你可以在这些事件里,通过抛出异常,促使工作单元回滚,阻止操作)。“ed”事件(例如EntityUpdated)在保存修改之后被触发,也就没有机会让工作单元回滚了。

触发事件

简单例子

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

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)

实体修改时事件的触发

在插入、更新或删除实体时,它们被ABP自动触发。如果你有一个Person实体,你可以注册EntityCreatedEventData,当一个新的Person创建并插入到数据库后,就可以收到通知。这些事件也支持继承,如果你有一个继承自Person的Student类,并且注册了EntityCreatedEventData,当一个Person或Student被插入后,你也会收到通知。

参阅 ABP框架 - 实体 ,使用AggregateRoot类的DomainEvents集合。

  1. AbpDbContext in Abp.EntityFramework

     public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken)
    {
    try
    {
    var changeReport = ApplyAbpConcepts();
    var result = await base.SaveChangesAsync(cancellationToken);
    await EntityChangeEventHelper.TriggerEventsAsync(changeReport);
    return result;
    }
    catch (DbEntityValidationException ex)
    {
    LogDbEntityValidationException(ex);
    throw;
    }
    } protected virtual EntityChangeReport ApplyAbpConcepts()
    {
    var changeReport = new EntityChangeReport(); var userId = GetAuditUserId(); var entries = ChangeTracker.Entries().ToList();
    foreach (var entry in entries)
    {
    switch (entry.State)
    {
    case EntityState.Added:
    CheckAndSetId(entry.Entity);
    CheckAndSetMustHaveTenantIdProperty(entry.Entity);
    CheckAndSetMayHaveTenantIdProperty(entry.Entity);
    SetCreationAuditProperties(entry.Entity, userId);
    changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Created));
    break;
    case EntityState.Modified:
    SetModificationAuditProperties(entry, userId);
    if (entry.Entity is ISoftDelete && entry.Entity.As<ISoftDelete>().IsDeleted)
    {
    SetDeletionAuditProperties(entry.Entity, userId);
    changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
    }
    else
    {
    changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Updated));
    } break;
    case EntityState.Deleted:
    CancelDeletionForSoftDelete(entry);
    SetDeletionAuditProperties(entry.Entity, userId);
    changeReport.ChangedEntities.Add(new EntityChangeEntry(entry.Entity, EntityChangeType.Deleted));
    break;
    } AddDomainEvents(changeReport.DomainEvents, entry.Entity);
    } return changeReport;
    } protected virtual void AddDomainEvents(List<DomainEventEntry> domainEvents, object entityAsObj)
    {
    var generatesDomainEventsEntity = entityAsObj as IGeneratesDomainEvents;
    if (generatesDomainEventsEntity == null)
    {
    return;
    } if (generatesDomainEventsEntity.DomainEvents.IsNullOrEmpty())
    {
    return;
    } domainEvents.AddRange(generatesDomainEventsEntity.DomainEvents.Select(eventData => new DomainEventEntry(entityAsObj, eventData)));
    generatesDomainEventsEntity.DomainEvents.Clear();
    }
  2. EntityChangeEventHelper in Abp.Events.Bus.Entities

     public class EntityChangeEventHelper : ITransientDependency, IEntityChangeEventHelper
    {
    public IEventBus EventBus { get; set; } private readonly IUnitOfWorkManager _unitOfWorkManager; public EntityChangeEventHelper(IUnitOfWorkManager unitOfWorkManager)
    {
    _unitOfWorkManager = unitOfWorkManager;
    EventBus = NullEventBus.Instance;
    } public virtual void TriggerEvents(EntityChangeReport changeReport)
    {
    TriggerEventsInternal(changeReport); if (changeReport.IsEmpty() || _unitOfWorkManager.Current == null)
    {
    return;
    } _unitOfWorkManager.Current.SaveChanges();
    } public Task TriggerEventsAsync(EntityChangeReport changeReport)
    {
    TriggerEventsInternal(changeReport); if (changeReport.IsEmpty() || _unitOfWorkManager.Current == null)
    {
    return Task.FromResult(0);
    } return _unitOfWorkManager.Current.SaveChangesAsync();
    } public virtual void TriggerEntityCreatingEvent(object entity)
    {
    TriggerEventWithEntity(typeof(EntityCreatingEventData<>), entity, true);
    } public virtual void TriggerEntityCreatedEventOnUowCompleted(object entity)
    {
    TriggerEventWithEntity(typeof(EntityCreatedEventData<>), entity, false);
    } public virtual void TriggerEntityUpdatingEvent(object entity)
    {
    TriggerEventWithEntity(typeof(EntityUpdatingEventData<>), entity, true);
    } public virtual void TriggerEntityUpdatedEventOnUowCompleted(object entity)
    {
    TriggerEventWithEntity(typeof(EntityUpdatedEventData<>), entity, false);
    } public virtual void TriggerEntityDeletingEvent(object entity)
    {
    TriggerEventWithEntity(typeof(EntityDeletingEventData<>), entity, true);
    } public virtual void TriggerEntityDeletedEventOnUowCompleted(object entity)
    {
    TriggerEventWithEntity(typeof(EntityDeletedEventData<>), entity, false);
    } public virtual void TriggerEventsInternal(EntityChangeReport changeReport)
    {
    TriggerEntityChangeEvents(changeReport.ChangedEntities);
    TriggerDomainEvents(changeReport.DomainEvents);
    } protected virtual void TriggerEntityChangeEvents(List<EntityChangeEntry> changedEntities)
    {
    foreach (var changedEntity in changedEntities)
    {
    switch (changedEntity.ChangeType)
    {
    case EntityChangeType.Created:
    TriggerEntityCreatingEvent(changedEntity.Entity);
    TriggerEntityCreatedEventOnUowCompleted(changedEntity.Entity);
    break;
    case EntityChangeType.Updated:
    TriggerEntityUpdatingEvent(changedEntity.Entity);
    TriggerEntityUpdatedEventOnUowCompleted(changedEntity.Entity);
    break;
    case EntityChangeType.Deleted:
    TriggerEntityDeletingEvent(changedEntity.Entity);
    TriggerEntityDeletedEventOnUowCompleted(changedEntity.Entity);
    break;
    default:
    throw new AbpException("Unknown EntityChangeType: " + changedEntity.ChangeType);
    }
    }
    } protected virtual void TriggerDomainEvents(List<DomainEventEntry> domainEvents)
    {
    foreach (var domainEvent in domainEvents)
    {
    EventBus.Trigger(domainEvent.EventData.GetType(), domainEvent.SourceEntity, domainEvent.EventData);
    }
    } protected virtual void TriggerEventWithEntity(Type genericEventType, object entity, bool triggerInCurrentUnitOfWork)
    {
    var entityType = entity.GetType();
    var eventType = genericEventType.MakeGenericType(entityType); if (triggerInCurrentUnitOfWork || _unitOfWorkManager.Current == null)
    {
    EventBus.Trigger(eventType, (IEventData)Activator.CreateInstance(eventType, new[] { entity }));
    return;
    } _unitOfWorkManager.Current.Completed += (sender, args) => EventBus.Trigger(eventType, (IEventData)Activator.CreateInstance(eventType, new[] { entity }));
    }
    }

事件的处理

处理一个事件,只要提供一个实现IEventHandler接口的类就可以了。

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

处理程序的自动注册

上例因为实现了ITransientDependency,它会被注册到Ioc容器,而ABP也会自动把它们注册到事件总线,当一个事件发生,ABP使用Ioc容器得到处理程序的引用,并在事件处理后释放该引用。在ABP里,这是使用事件总线的推荐的方式。

internal class EventBusInstaller : IWindsorInstaller
{
//……
public void Install(IWindsorContainer container, IConfigurationStore store)
{
if (_eventBusConfiguration.UseDefaultEventBus)
{
container.Register(
Component.For<IEventBus>().UsingFactoryMethod(() => EventBus.Default).LifestyleSingleton()
);
}
else
{
container.Register(
Component.For<IEventBus>().ImplementedBy<EventBus>().LifestyleSingleton()
);
} _eventBus = container.Resolve<IEventBus>();
container.Kernel.ComponentRegistered += Kernel_ComponentRegistered;
} private void Kernel_ComponentRegistered(string key, IHandler handler)
{
if (!typeof(IEventHandler).IsAssignableFrom(handler.ComponentModel.Implementation))
{
return;
} var interfaces = handler.ComponentModel.Implementation.GetInterfaces();
foreach (var @interface in interfaces)
{
if (!typeof(IEventHandler).IsAssignableFrom(@interface))
{
continue;
} var genericArgs = @interface.GetGenericArguments();
if (genericArgs.Length == 1)
{
_eventBus.Register(genericArgs[0], new IocHandlerFactory(_iocResolver, handler.ComponentModel.Implementation));
}
}
}
} public class IocHandlerFactory : IEventHandlerFactory
{
public Type HandlerType { get; private set; }
private readonly IIocResolver _iocResolver;
public IocHandlerFactory(IIocResolver iocResolver, Type handlerType)
{
_iocResolver = iocResolver;
HandlerType = handlerType;
} public IEventHandler GetHandler()
{
return (IEventHandler)_iocResolver.Resolve(HandlerType);
}
public void ReleaseHandler(IEventHandler handler)
{
_iocResolver.Release(handler);
}
}

手动注册

public class EventBus : IEventBus
{
/// <inheritdoc/>
public IDisposable Register<TEventData>(Action<TEventData> action) where TEventData : IEventData
{
return Register(typeof(TEventData), new ActionEventHandler<TEventData>(action));
}
/// <inheritdoc/>
public IDisposable Register<TEventData>(IEventHandler<TEventData> handler) where TEventData : IEventData
{
return Register(typeof(TEventData), handler);
}
/// <inheritdoc/>
public IDisposable Register<TEventData, THandler>()
where TEventData : IEventData
where THandler : IEventHandler<TEventData>, new()
{
return Register(typeof(TEventData), new TransientEventHandlerFactory<THandler>());
}
/// <inheritdoc/>
public IDisposable Register(Type eventType, IEventHandler handler)
{
return Register(eventType, new SingleInstanceHandlerFactory(handler));
}
/// <inheritdoc/>
public IDisposable Register<TEventData>(IEventHandlerFactory handlerFactory) where TEventData : IEventData
{
return Register(typeof(TEventData), handlerFactory);
}
/// <inheritdoc/>
public IDisposable Register(Type eventType, IEventHandlerFactory handlerFactory)
{
GetOrCreateHandlerFactories(eventType)
.Locking(factories => factories.Add(handlerFactory)); return new FactoryUnregistrar(this, eventType, handlerFactory);
}

事件总线的Register方法有几个重载,也就提供了多种注册方式。

接受一个委托

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

处理程序

internal class ActionEventHandler<TEventData> :
IEventHandler<TEventData>,
ITransientDependency
{
/// <summary>
/// Action to handle the event.
/// </summary>
public Action<TEventData> Action { get; private set; } /// <summary>
/// Creates a new instance of <see cref="ActionEventHandler{TEventData}"/>.
/// </summary>
/// <param name="handler">Action to handle the event</param>
public ActionEventHandler(Action<TEventData> handler)
{
Action = handler;
} /// <summary>
/// Handles the event.
/// </summary>
/// <param name="eventData"></param>
public void HandleEvent(TEventData eventData)
{
Action(eventData);
}
}

接受一个实现了IEventHantler的对象

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

注册为一个SingleInstanceHandlerFactory

internal class SingleInstanceHandlerFactory : IEventHandlerFactory
{ public IEventHandler HandlerInstance { get; private set; } public SingleInstanceHandlerFactory(IEventHandler handler)
{
HandlerInstance = handler;
} public IEventHandler GetHandler()
{
return HandlerInstance;
} public void ReleaseHandler(IEventHandler handler)
{ }
}

接受两个泛型参数

EventBus.Register<TaskCompletedEventData, ActivityWriter>();

注册为一个TransientEventHandlerFactory

internal class TransientEventHandlerFactory<THandler> : IEventHandlerFactory
where THandler : IEventHandler, new()
{
public IEventHandler GetHandler()
{
return new THandler();
}
public void ReleaseHandler(IEventHandler handler)
{
if (handler is IDisposable)
{
(handler as IDisposable).Dispose();
}
}
}

直接注册一个IEventHandlerFactory

上面的几种注册方式,最终都是在间接调用下面这个方法

    public IDisposable Register(Type eventType, IEventHandlerFactory handlerFactory)
{
GetOrCreateHandlerFactories(eventType)
.Locking(factories => factories.Add(handlerFactory)); return new FactoryUnregistrar(this, eventType, handlerFactory);
}

事件处理的取消注册

  1. 最简单的方式就是释放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();
  2. Unregister方法

     //Create a handler
    var handler = new ActivityWriter(); //Register to the event
    EventBus.Register<TaskCompletedEventData>(handler); //Unregister from event
    EventBus.Unregister<TaskCompletedEventData>(handler);
  3. EventBus提供了一个UnregisterAll()方法,它反注册一个事件的所有处理程序

  4. UnregisterAll()方法反注册所有事件的所有处理程序。

ABP文档笔记 - 事件BUS的更多相关文章

  1. ABP文档笔记系列

    ABP文档笔记 - 模块系统 及 配置中心 ABP文档笔记 - 事件BUS ABP文档笔记 - 数据过滤 ABP文档笔记 - 规约 ABP文档笔记 - 配置.设置.版本.功能.权限 ABP文档笔记 - ...

  2. ABP文档笔记 - 通知

    基础概念 两种通知发送方式 直接发送给目标用户 用户订阅某类通知,发送这类通知时直接分发给它们. 两种通知类型 一般通知:任意的通知类型 "如果一个用户发送一个好友请求,那么通知我&quot ...

  3. ABP文档笔记 - 数据过滤

    预定义的过滤 ISoftDelete 软删除过滤用来在查询数据库时,自动过滤(从结果中抽取)已删除的实体.如果一个实体可以被软删除,它必须实现ISoftDelete接口,该接口只定义了一个IsDele ...

  4. ABP文档笔记 - 配置、设置、版本、功能、权限

    配置 全局仅一个单例,保存一组配置信息,一般直接在模块的预启动事件中赋值or修改.没有Scope划分,无论租户还是房东亦或者用户读取的值都不会有差异.每个模块都可以扩展这个配置. 设置 它没有层级关系 ...

  5. ABP文档笔记 - 模块系统 及 配置中心

    ABP框架 - 模块系统 ABP框架 - 启动配置 Module System Startup Configuration ABP源码分析三:ABP Module ABP源码分析四:Configura ...

  6. ABP文档笔记 - 规约

    ABP框架 - 规约 简介 规约模式是一个特别的软件设计模式,业务逻辑可以使用boolean逻辑重新链接业务逻辑(*). 实践中的大部分情况,它是为实体或其它业务对象,定义可复用的过滤器. 理解 ...

  7. ABP文档 - 通知系统

    文档目录 本节内容: 简介 发送模式 通知类型 通知数据 通知重要性 关于通知持久化 订阅通知 发布通知 用户通知管理器 实时通知 客户端 通知存储 通知定义 简介 通知用来告知用户系统里特定的事件发 ...

  8. day56 文件 文档处理&comma;事件

    前情回顾: 1. 前情回顾 0. 选择器补充 - 属性选择器 - $("[egon]") - $("[type='text']") - $("inpu ...

  9. jQuery 选择器 筛选器 样式操作 文本操作 属性操作 文档处理 事件 动画效果 插件 each、data、Ajax

    jQuery jQuery介绍 1.jQuery是一个轻量级的.兼容多浏览器的JavaScript库. 2.jQuery使用户能够更方便地处理HTML Document.Events.实现动画效果.方 ...

随机推荐

  1. 宏定义中的&num;&num;操作符和&period;&period;&period; and &lowbar; &lowbar;VA&lowbar;ARGS&lowbar; &lowbar;

    1.Preprocessor Glue: The ## Operator 预处理连接符:##操作符 Like the # operator, the ## operator can be used i ...

  2. asp&period;net 页面url重写

    不更改情况下,页面路径为index.aspx?id=1,现在输入页面路径index/1时,也能访问到页面,这一过程叫做url重写 ①:在一个类里制定路径重写规则,以下为自定义UrlRewriterFi ...

  3. docker私有仓库

    1.docker pull registry 2.sudo docker run -d -p 5000:5000 registry 默认情况下,会将仓库存放于容器内的/tmp/registry目录下, ...

  4. &lbrack;LeetCode&rsqb;题解(python):151-Reverse Words in a String

    题目来源: https://leetcode.com/problems/reverse-words-in-a-string/ 题意分析: 给定一个字符串,里面包括多个单词,将这个字符串的单词翻转,例如 ...

  5. JavaScript高级程序设计:第二章

    在HTML中使用JavaScript 1.<script>元素:向HTML页面中插入JavaScript的主要方法就是使用<scritp>元素.HTML4.01为<scr ...

  6. React Native随笔——组件TextInput

    一.实例 先看一下我要做的搜索框的样子 需要一个Image,和一个TextInput 去掉默认下划线 underlineColorAndroid='transparent' 设置光标颜色 select ...

  7. POJ 3275 Ranking the Cows&lpar;传递闭包&rpar;【bitset优化Floyd】&plus;【领接表优化Floyd】

    <题目链接> 题目大意:FJ想按照奶牛产奶的能力给她们排序.现在已知有N头奶牛$(1 ≤ N ≤ 1,000)$.FJ通过比较,已经知道了M$1 ≤ M ≤ 10,000$对相对关系.每一 ...

  8. 串口USB单一映射及重命名

    本文针对在开发过程中有时会出现用到多个串口设备,usb端口号会发生变化,如设备的灯指示信号和其他控制器都是ttyUSB* .其序号与控制接入的顺序有关,对于写好的launch每次修改串口连接名很麻烦. ...

  9. darknet中的若干问题

    2018-04-20: https://github.com/pjreddie/darknet/issues/717 改了一下午,然后/usr/include/c++/4.8/bits/stl_rel ...

  10. redis相对关系型数据库的优势

    它是键值数据库(非关系),数据查询比关系型数据库快. ps:redis是树状结构,查询快 redis是基于内存的一个数据库,I/O的效率影响较小. ps: 备份数据同步是才进行I/O操作.这个数据同步 ...