【DDD-Apwork框架】事件总线和事件聚合器
第一步:事件总线和事件聚合器
【1】事件总线 IEventBus
IUnitOfWork.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; namespace Keasy5.Infrastructure
{
/// <summary>
/// 表示所有集成于该接口的类型都是Unit Of Work的一种实现。
/// </summary>
/// <remarks>有关Unit Of Work的详细信息,请参见UnitOfWork模式:http://martinfowler.com/eaaCatalog/unitOfWork.html。
/// </remarks>
public interface IUnitOfWork
{
/// <summary>
/// 获得一个<see cref="System.Boolean"/>值,
/// 该值表示当前的Unit Of Work是否支持Microsoft分布式事务处理机制。
/// </summary>
bool DistributedTransactionSupported { get; }
/// <summary>
/// 获得一个<see cref="System.Boolean"/>值,
/// 该值表述了当前的Unit Of Work事务是否已被提交。
/// </summary>
bool Committed { get; }
/// <summary>
/// 提交当前的Unit Of Work事务。
/// </summary>
void Commit();
/// <summary>
/// 回滚当前的Unit Of Work事务。
/// </summary>
void Rollback();
}
}
IBus.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Keasy5.Infrastructure; namespace Keasy5.Events.Bus
{
/// <summary>
/// Represents the message bus.
/// </summary>
public interface IBus : IUnitOfWork, IDisposable
{
Guid ID { get; }
/// <summary>
/// Publishes the specified message to the bus.
/// </summary>
/// <param name="message">The message to be published.</param>
void Publish<TMessage>(TMessage message)
where TMessage : class, IEvent;
/// <summary>
/// Publishes a collection of messages to the bus.
/// </summary>
/// <param name="messages">The messages to be published.</param>
void Publish<TMessage>(IEnumerable<TMessage> messages)
where TMessage : class, IEvent;
/// <summary>
/// Clears the published messages waiting for commit.
/// </summary>
void Clear();
}
}
接口: IEventBus.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace Keasy5.Events.Bus
{
public interface IEventBus : IBus
{
}
}
实现类:EventBus
EventBus.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Keasy5.Infrastructure; namespace Keasy5.Events.Bus
{
public class EventBus : DisposableObject, IEventBus
{
private readonly Guid id = Guid.NewGuid();
private readonly ThreadLocal<Queue<object>> messageQueue = new ThreadLocal<Queue<object>>(() => new Queue<object>());
private readonly IEventAggregator aggregator;
private ThreadLocal<bool> committed = new ThreadLocal<bool>(() => true);
private readonly MethodInfo publishMethod; /// <summary>
///
/// </summary>
/// <param name="aggregator"></param>
/// <remarks>
/// 参数IEventAggregator aggregator 的一个实现:EventAggregator<see cref="EventAggregator"/>
/// </remarks>
public EventBus(IEventAggregator aggregator)
{ this.aggregator = aggregator; //得到aggregator对象中的名为Publish的函数信息
// 1.相关资料:C# 反射泛型
// http://www.cnblogs.com/easy5weikai/p/3790589.html
publishMethod = (from m in aggregator.GetType().GetMethods()
let parameters = m.GetParameters()
let methodName = m.Name
where methodName == "Publish" &&
parameters != null &&
parameters.Length ==
select m).First();
} protected override void Dispose(bool disposing)
{
if (disposing)
{
messageQueue.Dispose();
committed.Dispose();
}
} #region IBus Members public void Publish<TMessage>(TMessage message)
where TMessage : class, IEvent
{
messageQueue.Value.Enqueue(message);
committed.Value = false;
} public void Publish<TMessage>(IEnumerable<TMessage> messages)
where TMessage : class, IEvent
{
foreach (var message in messages)
Publish(message);
} public void Clear()
{
messageQueue.Value.Clear();
committed.Value = true;
} #endregion #region IUnitOfWork Members public bool DistributedTransactionSupported
{
get { return false; }
} public bool Committed
{
get { return committed.Value; }
} public void Commit()
{
while (messageQueue.Value.Count > )
{
var evnt = messageQueue.Value.Dequeue();
var evntType = evnt.GetType();
//设置aggregator的publish方法的参数类型,
//子所以这样做,是因为存在重载,传人参数类型以确定哪个具体的方法。
var method = publishMethod.MakeGenericMethod(evntType);
//调用aggregator对象的publish
method.Invoke(aggregator, new object[] { evnt });
}
committed.Value = true;
} public void Rollback()
{
Clear();
} public Guid ID
{
get { return id; }
} #endregion
}
}
【2】事件聚合器IEventAggregator
接口:IEventAggregator.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace Keasy5.Events
{
public interface IEventAggregator
{
void Subscribe<TEvent>(IEventHandler<TEvent> domainEventHandler)
where TEvent : class, IEvent;
void Subscribe<TEvent>(IEnumerable<IEventHandler<TEvent>> domainEventHandlers)
where TEvent : class, IEvent;
void Subscribe<TEvent>(params IEventHandler<TEvent>[] domainEventHandlers)
where TEvent : class, IEvent;
void Subscribe<TEvent>(Action<TEvent> domainEventHandlerFunc)
where TEvent : class, IEvent;
void Subscribe<TEvent>(IEnumerable<Func<TEvent, bool>> domainEventHandlerFuncs)
where TEvent : class, IEvent;
void Subscribe<TEvent>(params Func<TEvent, bool>[] domainEventHandlerFuncs)
where TEvent : class, IEvent;
void Unsubscribe<TEvent>(IEventHandler<TEvent> domainEventHandler)
where TEvent : class, IEvent;
void Unsubscribe<TEvent>(IEnumerable<IEventHandler<TEvent>> domainEventHandlers)
where TEvent : class, IEvent;
void Unsubscribe<TEvent>(params IEventHandler<TEvent>[] domainEventHandlers)
where TEvent : class, IEvent;
void Unsubscribe<TEvent>(Action<TEvent> domainEventHandlerFunc)
where TEvent : class, IEvent;
void Unsubscribe<TEvent>(IEnumerable<Func<TEvent, bool>> domainEventHandlerFuncs)
where TEvent : class, IEvent;
void Unsubscribe<TEvent>(params Func<TEvent, bool>[] domainEventHandlerFuncs)
where TEvent : class, IEvent;
void UnsubscribeAll<TEvent>()
where TEvent : class, IEvent;
void UnsubscribeAll();
IEnumerable<IEventHandler<TEvent>> GetSubscriptions<TEvent>()
where TEvent : class, IEvent;
void Publish<TEvent>(TEvent domainEvent)
where TEvent : class, IEvent;
void Publish<TEvent>(TEvent domainEvent, Action<TEvent, bool, Exception> callback, TimeSpan? timeout = null)
where TEvent : class, IEvent;
} }
实现类:EventAggregator
EventAggregator.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks; namespace Keasy5.Events
{
public class EventAggregator : IEventAggregator
{
#region private property
private readonly object sync = new object();
private readonly Dictionary<Type, List<object>> eventHandlers = new Dictionary<Type, List<object>>();
private readonly MethodInfo registerEventHandlerMethod;
private readonly Func<object, object, bool> eventHandlerEquals = (o1, o2) =>
{
var o1Type = o1.GetType();
var o2Type = o2.GetType();
if (o1Type.IsGenericType &&
o1Type.GetGenericTypeDefinition() == typeof(ActionDelegatedEventHandler<>) &&
o2Type.IsGenericType &&
o2Type.GetGenericTypeDefinition() == typeof(ActionDelegatedEventHandler<>))
return o1.Equals(o2);
return o1Type == o2Type;
}; // checks if the two event handlers are equal. if the event handler is an action-delegated, just simply
// compare the two with the object.Equals override (since it was overriden by comparing the two delegates. Otherwise,
// the type of the event handler will be used because we don't need to register the same type of the event handler
// more than once for each specific event.
#endregion #region Ctor public EventAggregator()
{
registerEventHandlerMethod = (from p in this.GetType().GetMethods()
let methodName = p.Name
let parameters = p.GetParameters()
where methodName == "Subscribe" &&
parameters != null &&
parameters.Length == &&
parameters[].ParameterType.GetGenericTypeDefinition() == typeof(IEventHandler<>)
select p).First();
} /// <summary>
///
/// </summary>
/// <param name="handlers"></param>
/// <remarks>
/// 1.相关资料:C# 反射泛型
/// http://www.cnblogs.com/easy5weikai/p/3790589.html
/// 2. 依赖注入:
/// <!--Event Aggregator-->
/// <register type="Keasy5.Events.IEventAggregator, Keasy5.Events" mapTo="Keasy5.Events.EventAggregator, Keasy5.Events">
/// <constructor>
/// <param name="handlers">
/// <array>
/// <dependency name="orderDispatchedSendEmailHandler" type="Keasy5.Events.IEventHandler`1[[Keasy5.Domain.Events.OrderDispatchedEvent, Keasy5.Domain]], Keasy5.Events" />
/// <dependency name="orderConfirmedSendEmailHandler" type="Keasy5.Events.IEventHandler`1[[Keasy5.Domain.Events.OrderConfirmedEvent, Keasy5.Domain]], Keasy5.Events" />
/// </array>
/// </param>
/// </constructor>
/// </register>
/// </remarks>
public EventAggregator(object[] handlers)
: this()
{
foreach (var obj in handlers)
{
var type = obj.GetType();
var implementedInterfaces = type.GetInterfaces();
foreach (var implementedInterface in implementedInterfaces)
{
if (implementedInterface.IsGenericType &&
implementedInterface.GetGenericTypeDefinition() == typeof(IEventHandler<>))
{
var eventType = implementedInterface.GetGenericArguments().First();
var method = registerEventHandlerMethod.MakeGenericMethod(eventType);
method.Invoke(this, new object[] { obj });
}
}
}
}
#endregion #region interface IEventAggregator members
public void Subscribe<TEvent>(IEventHandler<TEvent> eventHandler)
where TEvent : class, IEvent
{
lock (sync)
{
var eventType = typeof(TEvent);
if (eventHandlers.ContainsKey(eventType))
{
var handlers = eventHandlers[eventType];
if (handlers != null)
{
if (!handlers.Exists(deh => eventHandlerEquals(deh, eventHandler)))
handlers.Add(eventHandler);
}
else
{
handlers = new List<object>();
handlers.Add(eventHandler);
}
}
else
eventHandlers.Add(eventType, new List<object> { eventHandler });
}
} public void Subscribe<TEvent>(IEnumerable<IEventHandler<TEvent>> eventHandlers)
where TEvent : class, IEvent
{
foreach (var eventHandler in eventHandlers)
Subscribe<TEvent>(eventHandler);
} public void Subscribe<TEvent>(params IEventHandler<TEvent>[] eventHandlers)
where TEvent : class, IEvent
{
foreach (var eventHandler in eventHandlers)
Subscribe<TEvent>(eventHandler);
} public void Subscribe<TEvent>(Action<TEvent> eventHandlerFunc)
where TEvent : class, IEvent
{
Subscribe<TEvent>(new ActionDelegatedEventHandler<TEvent>(eventHandlerFunc));
} public void Subscribe<TEvent>(IEnumerable<Func<TEvent, bool>> eventHandlerFuncs)
where TEvent : class, IEvent
{
foreach (var eventHandlerFunc in eventHandlerFuncs)
Subscribe<TEvent>(eventHandlerFunc);
} public void Subscribe<TEvent>(params Func<TEvent, bool>[] eventHandlerFuncs)
where TEvent : class, IEvent
{
foreach (var eventHandlerFunc in eventHandlerFuncs)
Subscribe<TEvent>(eventHandlerFunc);
} public void Unsubscribe<TEvent>(IEventHandler<TEvent> eventHandler)
where TEvent : class, IEvent
{
lock (sync)
{
var eventType = typeof(TEvent);
if (eventHandlers.ContainsKey(eventType))
{
var handlers = eventHandlers[eventType];
if (handlers != null &&
handlers.Exists(deh => eventHandlerEquals(deh, eventHandler)))
{
var handlerToRemove = handlers.First(deh => eventHandlerEquals(deh, eventHandler));
handlers.Remove(handlerToRemove);
}
}
}
} public void Unsubscribe<TEvent>(IEnumerable<IEventHandler<TEvent>> eventHandlers)
where TEvent : class, IEvent
{
foreach (var eventHandler in eventHandlers)
Unsubscribe<TEvent>(eventHandler);
} public void Unsubscribe<TEvent>(params IEventHandler<TEvent>[] eventHandlers)
where TEvent : class, IEvent
{
foreach (var eventHandler in eventHandlers)
Unsubscribe<TEvent>(eventHandler);
} public void Unsubscribe<TEvent>(Action<TEvent> eventHandlerFunc)
where TEvent : class, IEvent
{
Unsubscribe<TEvent>(new ActionDelegatedEventHandler<TEvent>(eventHandlerFunc));
} public void Unsubscribe<TEvent>(IEnumerable<Func<TEvent, bool>> eventHandlerFuncs)
where TEvent : class, IEvent
{
foreach (var eventHandlerFunc in eventHandlerFuncs)
Unsubscribe<TEvent>(eventHandlerFunc);
} public void Unsubscribe<TEvent>(params Func<TEvent, bool>[] eventHandlerFuncs)
where TEvent : class, IEvent
{
foreach (var eventHandlerFunc in eventHandlerFuncs)
Unsubscribe<TEvent>(eventHandlerFunc);
} public void UnsubscribeAll<TEvent>()
where TEvent : class, IEvent
{
lock (sync)
{
var eventType = typeof(TEvent);
if (eventHandlers.ContainsKey(eventType))
{
var handlers = eventHandlers[eventType];
if (handlers != null)
handlers.Clear();
}
}
} public void UnsubscribeAll()
{
lock (sync)
{
eventHandlers.Clear();
}
} public IEnumerable<IEventHandler<TEvent>> GetSubscriptions<TEvent>()
where TEvent : class, IEvent
{
var eventType = typeof(TEvent);
if (eventHandlers.ContainsKey(eventType))
{
var handlers = eventHandlers[eventType];
if (handlers != null)
return handlers.Select(p => p as IEventHandler<TEvent>).ToList();
else
return null;
}
else
return null;
} public void Publish<TEvent>(TEvent evnt)
where TEvent : class, IEvent
{
if (evnt == null)
throw new ArgumentNullException("evnt");
var eventType = evnt.GetType();
if (eventHandlers.ContainsKey(eventType) &&
eventHandlers[eventType] != null &&
eventHandlers[eventType].Count > )
{
var handlers = eventHandlers[eventType];
foreach (var handler in handlers)
{
var eventHandler = handler as IEventHandler<TEvent>;
if (eventHandler == null)
throw new ArgumentNullException("eventHandler");
if (eventHandler.GetType().IsDefined(typeof(HandlesAsynchronouslyAttribute), false))
{
Task.Factory.StartNew((o) => eventHandler.Handle((TEvent)o), evnt);
}
else
{
eventHandler.Handle(evnt);
}
}
}
} public void Publish<TEvent>(TEvent evnt,
Action<TEvent, bool, Exception> callback,
TimeSpan? timeout = null)
where TEvent : class, IEvent
{
if (evnt == null)
throw new ArgumentNullException("evnt");
var eventType = evnt.GetType();
if (eventHandlers.ContainsKey(eventType) &&
eventHandlers[eventType] != null &&
eventHandlers[eventType].Count > )
{
var handlers = eventHandlers[eventType];
List<Task> tasks = new List<Task>();
try
{
foreach (var handler in handlers)
{
var eventHandler = handler as IEventHandler<TEvent>;
if (eventHandler.GetType().IsDefined(typeof(HandlesAsynchronouslyAttribute), false))
{
tasks.Add(Task.Factory.StartNew((o) => eventHandler.Handle((TEvent)o), evnt));
}
else
{
eventHandler.Handle(evnt);
}
}
if (tasks.Count > )
{
if (timeout == null)
Task.WaitAll(tasks.ToArray());
else
Task.WaitAll(tasks.ToArray(), timeout.Value);
}
callback(evnt, true, null);
}
catch (Exception ex)
{
callback(evnt, false, ex);
}
}
else
callback(evnt, false, null);
}
#endregion
}
}
第二步:使用:
private readonly IEventBus eventBus;
。。。。
eventBus.Publish<OrderDispatchedEvent>(evnt);
【DDD-Apwork框架】事件总线和事件聚合器的更多相关文章
- Autofac解耦事件总线
事件总线之Autofac解耦 事件总线是通过一个中间服务,剥离了常规事件的发布与订阅(消费)强依赖关系的一种技术实现.事件总线的基础知识可参考圣杰的博客[事件总线知多少] 本片博客不再详细概述事件总线 ...
- ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线
在上文中,我们讨论了事件处理器中对象生命周期的问题,在进入新的讨论之前,首先让我们总结一下,我们已经实现了哪些内容.下面的类图描述了我们已经实现的组件及其之间的关系,貌似系统已经变得越来越复杂了. 其 ...
- ABP官方文档翻译 3.7 领域事件(事件总线)
领域事件(事件总线) 事件总线 注入IEventBus 获取默认实例 定义事件 预定义事件 处理异常 实体更改 触发事件 处理事件 处理基础事件 处理者异常 处理多个事件 注册处理者 自动 手动 取消 ...
- SOFA 源码分析— 事件总线
前言 大部分框架都是事件订阅功能,即观察者模式,或者叫事件机制.通过订阅某个事件,当触发事件时,回调某个方法.该功能非常的好用,而 SOFA 内部也设计了这个功能,并且内部大量使用了该功能.来看看是如 ...
- .Net 事件总线之Autofac解耦
事件总线是通过一个中间服务,剥离了常规事件的发布与订阅(消费)强依赖关系的一种技术实现.事件总线的基础知识可参考圣杰的博客[事件总线知多少] 本片博客不再详细概述事件总线基础知识,核心点放置使用Aut ...
- RabbitMQ的事件总线
RabbitMQ的事件总线 在上文中,我们讨论了事件处理器中对象生命周期的问题,在进入新的讨论之前,首先让我们总结一下,我们已经实现了哪些内容.下面的类图描述了我们已经实现的组件及其之间的关系,貌似系 ...
- 重温.NET下Assembly的加载过程 ASP.NET Core Web API下事件驱动型架构的实现(三):基于RabbitMQ的事件总线
重温.NET下Assembly的加载过程 最近在工作中牵涉到了.NET下的一个古老的问题:Assembly的加载过程.虽然网上有很多文章介绍这部分内容,很多文章也是很久以前就已经出现了,但阅读之后 ...
- [Abp vNext 源码分析] - 13. 本地事件总线与分布式事件总线 (Rabbit MQ)
一.简要介绍 ABP vNext 封装了两种事件总线结构,第一种是 ABP vNext 自己实现的本地事件总线,这种事件总线无法跨项目发布和订阅.第二种则是分布式事件总线,ABP vNext 自己封装 ...
- Android事件总线
Android中Activity.Service.Fragment之间的相互通信比较麻烦,主要有以下一些方法: (1)使用广播,发送者发出广播,接收者接收广播后进行处理: (2)使用Handler和M ...
随机推荐
- C#.NET实现Word或Excel文件转为HTML文件
Word文件转html,返回相对路径 private string GetPathByDocToHTML(string strFile) { if (string.IsNullOrEmpty(strF ...
- python:笔记for循环中的else
我们常常会在for循环遍历一个序列或者字典后,接着语句else,很多新手会误以为是判断执行else后面的 代码,其实不然,for循环里面也不存在判断,而已执行完遍历的对象后,再执行else后面的代码. ...
- 20140215-搭建phonegap开发环境
1.安装jdk,配置环境变量 a)设置 JAVA_HOME =E:\SetUpFiles\win8.1\Java\jdk1.6.0_45 b)设置PATH=E:\SetUpFiles\win8.1\J ...
- 《HTML5与CSS3基础教程》学习笔记 ——One Day
第一章 1. 邮箱地址的URL地址包括:mailto:+邮箱地址 2. ../表示向上走一级,开头直接使用/表示根目录 第三章 1. <header>: role = “ ...
- copy con
在DOS系统中,一些计算机设备也有文件名,叫做设备文件,可以用对应的文件名来操作它.后来的WINDOWS也保留了这些设备文件.比如con表示console(控制台,简单的说就是键盘和屏幕),com ...
- 第一章 C++简介
第一章 C++简介 1.1 C++特点 C++融合了3种不同的编程方式:C语言代表的过程性语言,C++在C语言基础上添加的类代表的面向对象语言,C++模板支持的泛型编程. 1.2 C语言及其编程 ...
- 【风马一族_xml】xml的基本讲解笔记
xml是如何保存数据的 在xml语言中,它允许用户自定义标签.每个标签用于描述一段数据; 一个标签可以分为开始标签和结束标签,在开始标签和结束标签之间又可以嵌套其它标签,利用标签间的嵌套其它标签,利用 ...
- eclipse 最全快捷键(网络收集)
Ctrl+1 快速修复(最经典的快捷键,就不用多说了) Ctrl+D: 删除当前行 Ctrl+Alt+↓ 复制当前行到下一行(复制增加) Ctrl+Alt+↑ 复制当前行到上一行(复制增加) Alt+ ...
- WPF 类型“System.ComponentModel.ISupportInitialize”在未被引用的程序集中定义。
问题:类型“System.ComponentModel.ISupportInitialize”在未被引用的程序集中定义.必须添加对程序集“System, Version=4.0.0.0, Cultur ...
- centos系统下安装使用composer教程
Composer 是 PHP 的一个依赖管理工具.它允许你申明项目所依赖的代码库,它会在你的项目中为你安装他们.Composer 不是一个包管理器.是的,它涉及 "packages" ...