ABP文檔筆記 - 事件BUS

文檔:html

EventBus(事件總線)

EventBus是一個單例對象,被全部類觸發事件或處理事件時共享。數據庫

IEventBusConfiguration在應用啓動時加載(by AbpCoreInstaller),依據它的配置決定 是全部IWindsorContainer實例共享同一條事件總線(EventBus.Default),仍是每一個IWindsorContainer實例建立一個本身的單例。默認使用EventBus.Default。
框架

由於在應用中IocManager使用單例,IIocManager.IocContainer也是單一的,因此事件總線使用的就是EventBus.Default。dom

注入 IEventBus

使用屬性注入模式:async

public class TaskAppService : ApplicationService
{
    public IEventBus EventBus { get; set; }

    public TaskAppService()
    {
        EventBus = NullEventBus.Instance;
    }
}

直接使用默認實例

若是你不能注入它,能夠直接使用EventBus.Default。使用方式以下所示:ide

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

但就如同不推薦使用IocManager.Instance同樣,在任何可能的地方都不建議直接使用EventBus.Default,由於它難於單元測試。源碼分析

定義事件

事件定義,繼承EventData

在觸發一個事件前,你首先要定義它,經過一個繼承自EventData的類來表現一個事件。單元測試

假設當一個任務完成後咱們想觸發一個事件:測試

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

這個類包含處理事件類所須要的屬性,EventData類定義了EventSource(事件源,哪一個對象觸發了事件)和EventTime(什麼時候觸發的)屬性。
this

泛型的事件定義,使用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()方法反註冊全部事件的全部處理程序。

相關文章
相關標籤/搜索