Lind.DDD.Events事件总线~自动化注册
让大叔兴奋的自动化注册
对于领域事件之前说过,在程序启动时订阅(注册)一些事件处理程序,然后在程序的具体位置去发布(触发)它,这是传统的pub/sub模式的体现,当然也没有什么问题,为了让它支持分布式的场景,我们引用了redis这种存储介质,这一切都早已集成到了Lind.DDD架构中,对些没什么好说的,而今天的重点在于"事件的自动过注册"的理念,这个概念真实在ABP架构中出现了,大叔觉得很不错,所以也集成到了自己的架构中,为些也兴奋了一段时间,其中有解决问题的
Redis只是一种分布式存储介质
对于第一版将事件总线放到内存的情况来说,使用redis这种存储介质确实解决了分布式的事件问题,它可以在更多场合下使用,不用考虑WEB端的负载均衡,不用考虑服务端的存储压力,不用考虑并发时的吞吐量,确实,redis是个存储效率非常高的产物,大叔redis里的事件的Key采用了当前EventData的名字加上自定义的前缀,这样可以同时在多个项目中使用.
/// <summary> /// 对于事件数据的存储,目前采用内存字典 /// </summary> private readonly IRedisClient _redisClient = RedisManager.GetClient(); /// <summary> /// redis事件总线的Key /// </summary> private string redisKey = ConfigConstants.ConfigManager.Config.DomainEvent.RedisKey; /// <summary> /// 得到当前redis-eventbus-key /// </summary> /// <typeparam name="TEvent"></typeparam> /// <returns></returns> private string GetCurrentRedisKey<TEvent>() { return redisKey + "_" + typeof(TEvent).FullName; } /// <summary> ///得到非泛型版本的值 /// </summary> /// <param name="tEvent"></param> /// <returns></returns> private string GetCurrentRedisKey(Type tEvent) { return redisKey + "_" + tEvent.FullName; }
结构图
主角是SubscribeAll这个方法
对于当前应用程序下的所有DLL进行反射,拿到所有实现了IEventHandler的类型,然后对这么类型(事件处理程序)进行注册即可.
核心代码(Memory版):
/// <summary> /// 需要过滤的接口 /// </summary> string[] Excepts = { "IEventHandler`1", "ActionDelegatedEventHandler`1" }; /// <summary> /// 全局统一注册所有事件处理程序,实现了IEventHandlers的 /// </summary> public void SubscribeAll() { var types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes() .Where(t => t.GetInterfaces().Contains(typeof(IEventHandlers)))) .Where(i => !Excepts.Contains(i.Name)) .ToArray(); foreach (var item in types) { if (!item.ContainsGenericParameters) { var en = Activator.CreateInstance(item); foreach (var t in item.GetInterfaces().Where(i => i.Name != "IEventHandlers")) { Subscribe(t, en); } } } } /// <summary> /// 订阅非泛型版 /// </summary> /// <param name="type"></param> /// <param name="eventHandler"></param> void Subscribe(Type type, object eventHandler) { lock (_objLock) { var eventType = type.GetGenericArguments()[0]; //var eventType = type.GetType().GenericTypeArguments[0]; 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 }); } }
对于这种仓储,在Redis里事实上是以二进制的格式存储的,所以要求你的EventData和EventHandler需要标记为可序列化,我经过测试,对于Json序列化的方式,在进行发布时,不能成功回调"订阅"的代码,原因我目前还不清楚,需要大家一起去研究!
感谢各位的阅读!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
2014-06-12 网页中对图像的采集
2014-06-12 说说设计模式~适配器模式(Adapter)
2012-06-12 基础才是重中之重~B/S系统中的事件订阅
2012-06-12 基础才是重中之重~延迟初始化