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

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

我一直都认为,字典这种数据结构是个很好东西,简单而且强大,利用好了真是事半功倍。说到事件的时候,我们一定也要想到委托,在 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 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • Electron

    Electron 基于 Chromium 和 Node.js,让你可以使用 HTML、CSS 和 JavaScript 构建应用。它是一个由 GitHub 及众多贡献者组成的活跃社区共同维护的开源项目,兼容 Mac、Windows 和 Linux,它构建的应用可在这三个操作系统上面运行。

    15 引用 • 136 回帖 • 6 关注
  • 禅道

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

    6 引用 • 15 回帖 • 44 关注
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 639 关注
  • 生活

    生活是指人类生存过程中的各项活动的总和,范畴较广,一般指为幸福的意义而存在。生活实际上是对人生的一种诠释。生活包括人类在社会中与自己息息相关的日常活动和心理影射。

    230 引用 • 1454 回帖
  • GraphQL

    GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。

    4 引用 • 3 回帖
  • Linux

    Linux 是一套免费使用和自由传播的类 Unix 操作系统,是一个基于 POSIX 和 Unix 的多用户、多任务、支持多线程和多 CPU 的操作系统。它能运行主要的 Unix 工具软件、应用程序和网络协议,并支持 32 位和 64 位硬件。Linux 继承了 Unix 以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。

    950 引用 • 943 回帖 • 1 关注
  • LeetCode

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

    209 引用 • 72 回帖
  • danl
    163 关注
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1063 引用 • 3455 回帖 • 166 关注
  • RemNote
    2 引用 • 16 回帖 • 9 关注
  • Vue.js

    Vue.js(读音 /vju ː/,类似于 view)是一个构建数据驱动的 Web 界面库。Vue.js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件。

    267 引用 • 666 回帖 • 1 关注
  • 链书

    链书(Chainbook)是 B3log 开源社区提供的区块链纸质书交易平台,通过 B3T 实现共享激励与价值链。可将你的闲置书籍上架到链书,我们共同构建这个全新的交易平台,让闲置书籍继续发挥它的价值。

    链书社

    链书目前已经下线,也许以后还有计划重制上线。

    14 引用 • 257 回帖 • 3 关注
  • RYMCU

    RYMCU 致力于打造一个即严谨又活泼、专业又不失有趣,为数百万人服务的开源嵌入式知识学习交流平台。

    4 引用 • 6 回帖 • 54 关注
  • Swagger

    Swagger 是一款非常流行的 API 开发工具,它遵循 OpenAPI Specification(这是一种通用的、和编程语言无关的 API 描述规范)。Swagger 贯穿整个 API 生命周期,如 API 的设计、编写文档、测试和部署。

    26 引用 • 35 回帖 • 3 关注
  • 导航

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

    43 引用 • 177 回帖
  • 星云链

    星云链是一个开源公链,业内简单的将其称为区块链上的谷歌。其实它不仅仅是区块链搜索引擎,一个公链的所有功能,它基本都有,比如你可以用它来开发部署你的去中心化的 APP,你可以在上面编写智能合约,发送交易等等。3 分钟快速接入星云链 (NAS) 测试网

    3 引用 • 16 回帖
  • 996
    13 引用 • 200 回帖
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    245 引用 • 1338 回帖 • 1 关注
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    21 引用 • 245 回帖 • 235 关注
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 718 关注
  • Q&A

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

    9345 引用 • 42566 回帖 • 114 关注
  • 架构

    我们平时所说的“架构”主要是指软件架构,这是有关软件整体结构与组件的抽象描述,用于指导软件系统各个方面的设计。另外还有“业务架构”、“网络架构”、“硬件架构”等细分领域。

    143 引用 • 442 回帖
  • Postman

    Postman 是一款简单好用的 HTTP API 调试工具。

    4 引用 • 3 回帖 • 1 关注
  • Rust

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

    58 引用 • 22 回帖 • 2 关注
  • JRebel

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

    26 引用 • 78 回帖 • 678 关注
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    179 引用 • 408 回帖 • 486 关注
  • 新人

    让我们欢迎这对新人。哦,不好意思说错了,让我们欢迎这位新人!
    新手上路,请谨慎驾驶!

    52 引用 • 228 回帖