dotnet 应用程序级别的事件发布和订阅

本贴最后更新于 2052 天前,其中的信息可能已经事过景迁

我一直都认为,字典这种数据结构是个很好东西,简单而且强大,利用好了真是事半功倍。说到事件的时候,我们一定也要想到委托,在 net 的事件里,事件就是多播委托,这篇文章的角色主要就是字典和委托了。
在一个小项目中,对于数据的精准性可能要求不太严格,但是也会存在这样的逻辑,比如下单之后,记录日志,增加用户订单量,等等其他一些不必再事务中处理的操作(事务中处理的步骤越少越好),这些逻辑操作就算失败一两个也没什么关系,这个时候就没必要使用外部队列来确保执行了,这种情况下使用这篇文章说的应用程序内的事件发布和订阅就比较合适。
我们先定义事件管理器:

public class AppEventService { private readonly ConcurrentDictionary<string, Func<object[], object>> _eventHandlerDict = new ConcurrentDictionary<string, Func<object[], object>>(); }

其中的字典便是存储委托的容器,再提供一个添加订阅和发布的方法如下:

/// <summary> /// 添加事件处理程序 /// </summary> /// <param name="eventKey"></param> /// <param name="func"></param> public void AddHandler(string eventKey, Func<object[], object> func) { _eventHandlerDict.AddOrUpdate(eventKey, func, (key, oldFun) => { return oldFun += func; }); } /// <summary> /// 触发事件 /// </summary> /// <param name="eventKey"></param> /// <param name="ps"></param> /// <returns></returns> public Task Fire(string eventKey, params object[] ps) { return _eventHandlerDict.TryGetValue(eventKey, out var func) ? Task.Run(() => func(ps)) : Task.CompletedTask; }

基本功能是实现了,但是由于事件处理的委托需要一个一个添加,也是挺麻烦的,所以我们定义一个特性类,来标识应用程序内的事件处理程序:

/// <summary> /// 标记方法是appEvent的事件处理程序 /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] public class AppEventHandlerAttribute : Attribute { public AppEventHandlerAttribute(string eventKey) { EventKey = eventKey; } /// <summary> /// key /// </summary> public string EventKey { get; set; } }

有了标识的特性类,我们就可以在 AppEventService 添加一个批量扫描的方法:

/// <summary> /// 扫码程序集,注册事件处理程序 /// </summary> /// <param name="assembly"></param> public void ScanEventHandler(Assembly assembly) { foreach (var type in assembly.GetTypes()) { var methodInfos = type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic ^ BindingFlags.GetProperty ^ BindingFlags.SetProperty); foreach (var methodInfo in methodInfos) { var mehAttr = methodInfo.GetCustomAttribute<AppEventHandlerAttribute>(); if (mehAttr == null) continue; var fun = DynamicMethodHelper.GetExecuteDelegate(methodInfo); AddHandler(mehAttr.EventKey, (args) => fun(methodInfo.IsStatic ? null : Activator.CreateInstance(type), args)); } } }

在 asp.netcore 中,框架自带了 DI,我们再添加的代码来实现从容器中加载:

private IServiceProvider _serviceProvider; /// <summary> /// 实例化AppEventService /// </summary> public AppEventService() { } public void SetServiceProvider(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } /// <summary> /// 扫描容器中的服务,注册时间处理程序 /// </summary> /// <param name="services"></param> public void ScanEventHandler(IServiceCollection services) { foreach (var service in services) { var methodInfos = service.ServiceType.GetMethods( BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic ^ BindingFlags.GetProperty ^ BindingFlags.SetProperty); foreach (var methodInfo in methodInfos) { var mehAttr = methodInfo.GetCustomAttribute<AppEventHandlerAttribute>(); if (mehAttr == null) continue; var fun = DynamicMethodHelper.GetExecuteDelegate(methodInfo); AddHandler(mehAttr.EventKey, (args) => fun(methodInfo.IsStatic ? null : _serviceProvider.GetService(service.ServiceType), args)); } } }

然后安装常规约定,我们提供一个扩展方法来添加 AppEventService:

public static class AppEventExtensions { /// <summary> /// 注册应用程序域中所有有AppService特性的类 /// </summary> /// <param name="services"></param> public static void AddAppEvents(this IServiceCollection services) { AppEventService appEventService = new AppEventService(); appEventService.ScanEventHandler(services); services.AddSingleton(appEventService); } }
  • .NET
    27 引用 • 6 回帖 • 5 关注
  • 事件
    6 引用 • 43 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • Flume

    Flume 是一套分布式的、可靠的,可用于有效地收集、聚合和搬运大量日志数据的服务架构。

    9 引用 • 6 回帖 • 650 关注
  • 安装

    你若安好,便是晴天。

    132 引用 • 1184 回帖 • 1 关注
  • OpenShift

    红帽提供的 PaaS 云,支持多种编程语言,为开发人员提供了更为灵活的框架、存储选择。

    14 引用 • 20 回帖 • 642 关注
  • Sillot

    Insights(注意当前设置 master 为默认分支)

    汐洛彖夲肜矩阵(Sillot T☳Converbenk Matrix),致力于服务智慧新彖乄,具有彖乄驱动、极致优雅、开发者友好的特点。其中汐洛绞架(Sillot-Gibbet)基于自思源笔记(siyuan-note),前身是思源笔记汐洛版(更早是思源笔记汐洛分支),是智慧新录乄终端(多端融合,移动端优先)。

    主仓库地址:Hi-Windom/Sillot

    文档地址:sillot.db.sc.cn

    注意事项:

    1. ⚠️ 汐洛仍在早期开发阶段,尚不稳定
    2. ⚠️ 汐洛并非面向普通用户设计,使用前请了解风险
    3. ⚠️ 汐洛绞架基于思源笔记,开发者尽最大努力与思源笔记保持兼容,但无法实现 100% 兼容
    29 引用 • 25 回帖 • 96 关注
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 683 关注
  • FreeMarker

    FreeMarker 是一款好用且功能强大的 Java 模版引擎。

    23 引用 • 20 回帖 • 457 关注
  • 人工智能

    人工智能(Artificial Intelligence)是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门技术科学。

    142 引用 • 209 回帖 • 2 关注
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖 • 2 关注
  • 导航

    各种网址链接、内容导航。

    43 引用 • 177 回帖 • 2 关注
  • Docker

    Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的操作系统上。容器完全使用沙箱机制,几乎没有性能开销,可以很容易地在机器和数据中心中运行。

    493 引用 • 928 回帖
  • 负能量

    上帝为你关上了一扇门,然后就去睡觉了....努力不一定能成功,但不努力一定很轻松 (° ー °〃)

    88 引用 • 1235 回帖 • 408 关注
  • LeetCode

    LeetCode(力扣)是一个全球极客挚爱的高质量技术成长平台,想要学习和提升专业能力从这里开始,充足技术干货等你来啃,轻松拿下 Dream Offer!

    209 引用 • 72 回帖 • 2 关注
  • 博客

    记录并分享人生的经历。

    273 引用 • 2388 回帖
  • 微软

    微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。

    8 引用 • 44 回帖 • 1 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖
  • 招聘

    哪里都缺人,哪里都不缺人。

    190 引用 • 1057 回帖
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    29 引用 • 66 回帖
  • Python

    Python 是一种面向对象、直译式电脑编程语言,具有近二十年的发展历史,成熟且稳定。它包含了一组完善而且容易理解的标准库,能够轻松完成很多常见的任务。它的语法简捷和清晰,尽量使用无异义的英语单词,与其它大多数程序设计语言使用大括号不一样,它使用缩进来定义语句块。

    548 引用 • 674 回帖
  • Eclipse

    Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。就其本身而言,它只是一个框架和一组服务,用于通过插件组件构建开发环境。

    75 引用 • 258 回帖 • 631 关注
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 384 回帖 • 4 关注
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    6 引用 • 15 回帖 • 67 关注
  • Ant-Design

    Ant Design 是服务于企业级产品的设计体系,基于确定和自然的设计价值观上的模块化解决方案,让设计者和开发者专注于更好的用户体验。

    17 引用 • 23 回帖 • 8 关注
  • SQLite

    SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是全世界使用最为广泛的数据库引擎。

    5 引用 • 7 回帖
  • Ubuntu

    Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。

    127 引用 • 169 回帖 • 2 关注
  • TensorFlow

    TensorFlow 是一个采用数据流图(data flow graphs),用于数值计算的开源软件库。节点(Nodes)在图中表示数学操作,图中的线(edges)则表示在节点间相互联系的多维数据数组,即张量(tensor)。

    20 引用 • 19 回帖 • 3 关注
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖 • 2 关注
  • Q&A

    提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。

    8997 引用 • 40990 回帖 • 129 关注