| 名称 | NuGet | 下载量 |
|---|---|---|
| CodeWF.EventBus | ||
| CodeWF.IOC.EventBus | ||
| CodeWF.DryIoc.EventBus | ||
| CodeWF.AspNetCore.EventBus |
- 当前版本:
3.4.5.8,版本号统一维护在根目录Directory.Build.props的<Version>节点。 - NuGet 包项目统一支持
net8.0;net10.0;Demo、App、测试与内部应用项目统一使用net11.0/net11.0-windows。 - 根目录
logo.svg、logo.png、logo.ico是唯一图标源,子工程只通过 MSBuildLink引用,不维护图标副本。 - 运行时帮助、Markdown 示例、内置备忘录、设计说明等业务文档按功能保留;仓库级入口文档使用根目录
README.md和UpdateLog.md。
CodeWF.EventBus 是一个轻量的进程内事件总线库,适合在 WPF、WinForms、Avalonia UI、ASP.NET Core 和控制台程序中做模块解耦。
它支持两类典型场景:
Command命令分发。Query<T>查询回传,方便实现简单 CQRS。
如果你熟悉 MediatR、Prism.Events 或 MASA Framework 的事件处理方式,可以把它理解成一个更轻量、对项目类型约束更少的选择。
设计说明可查看:
按项目类型选择包:
- 无 IOC 容器:
CodeWF.EventBus - ASP.NET Core / MS.DI:
CodeWF.AspNetCore.EventBus - DryIoc / Prism:
CodeWF.DryIoc.EventBus - 其他 IOC 容器:
CodeWF.IOC.EventBus
publicabstractclassCommand{}publicabstractclassQuery<TResponse>:Command{publicabstractTResponseResult{get;set;}}示例:
publicsealedclassCreateProductCommand:Command{publicstringName{get;set;}=string.Empty;publicdecimalPrice{get;set;}}publicsealedclassProductQuery:Query<ProductItemDto?>{publicGuidProductId{get;set;}publicoverrideProductItemDto?Result{get;set;}}事件处理方法使用 [EventHandler] 标记,参数只能有一个,且必须继承自 Command。返回值只支持:
voidTask
方法声明支持:
publicprivatestatic
说明:
Subscribe<T>()/Subscribe(Type)会扫描指定类型中的public/private static处理方法。Subscribe(this)会扫描当前实例中的public/private instance处理方法。Subscribe(Assembly[])会登记标记了[Event]的类型中public/private instance处理方法,真正执行时再通过服务解析器拿实例。
示例:
[Event]publicsealedclassProductEventHandler{[EventHandler]privateasyncTaskHandleCreateAsync(CreateProductCommandcommand){awaitTask.CompletedTask;}[EventHandler]privatevoidHandleQuery(ProductQueryquery){query.Result=newProductItemDto{Id=query.ProductId,Name="Demo",Price=99};}}[Event] 主要用于 IOC 自动发现实例处理器。通过 Subscribe<T>() 这类方式扫描指定类型时,不需要再额外标记 [Event]:
publicstaticclassTimeHandler{[EventHandler]privatestaticvoidHandle(UpdateTimeCommandcommand){Console.WriteLine(command.Time);}}WPF、WinForms、Avalonia UI、控制台等未接入 IOC 时,建议直接使用 EventBus.Default 或自己 new 一个 EventBus。
publicsealedclassMainViewModel{privatereadonlyIEventBus_eventBus;publicMainViewModel(){_eventBus=EventBus.Default;_eventBus.Subscribe(this);}[EventHandler]privatevoidHandle(UpdateTimeCommandcommand){Console.WriteLine(command.Time);}}vareventBus=EventBus.Default;eventBus.Subscribe<TimeHandler>();eventBus.Publish(newUpdateTimeCommand("2026-04-26 10:00:00"));awaitEventBus.Default.PublishAsync(newCreateProductCommand{Name="XiaoMi",Price=8999});varproduct=awaitEventBus.Default.QueryAsync(newProductQuery{ProductId=Guid.NewGuid()});实例对象不再使用时,建议主动取消订阅:
EventBus.Default.Unsubscribe(this);通过扫描指定类型注册的处理器也可以取消:
EventBus.Default.Unsubscribe<TimeHandler>();安装 CodeWF.AspNetCore.EventBus 后,在 Program.cs 中注册:
usingCodeWF.AspNetCore.EventBus;varbuilder=WebApplication.CreateBuilder(args);builder.Services.AddControllers();builder.Services.AddScoped<IProductService,ProductService>();builder.Services.AddEventBus();varapp=builder.Build();app.MapControllers();app.UseEventBus();app.Run();说明:
AddEventBus()会扫描程序集中的[Event]类,并将它们按作用域注册到容器中。UseEventBus()会把扫描指定类型得到的处理器和实例处理器都接入事件总线。
控制器中直接注入 IEventBus:
[ApiController][Route("[controller]")]publicclassEventController:ControllerBase{privatereadonlyIEventBus_eventBus;publicEventController(IEventBuseventBus){_eventBus=eventBus;}[HttpPost("/add")]publicasyncTaskAddAsync([FromBody]CreateProductRequestrequest){await_eventBus.PublishAsync(newCreateProductCommand{Name=request.Name,Price=request.Price});}[HttpGet("/get")]publicasyncTask<ActionResult<ProductItemDto>>GetAsync([FromQuery]Guidid){varproduct=await_eventBus.QueryAsync(newProductQuery{ProductId=id});returnproduct==null?NotFound():Ok(product);}}安装 CodeWF.DryIoc.EventBus 后:
protectedoverridevoidRegisterTypes(IContainerRegistrycontainerRegistry){varcontainer=containerRegistry.GetContainer();containerRegistry.AddEventBus();container.UseEventBus();}安装 CodeWF.IOC.EventBus 后,把你的“注册单例 / 注册作用域 / 按类型解析”能力传进去:
usingCodeWF.IOC.EventBus;EventBusExtensions.AddEventBus((serviceType,implementationType)=>builder.Services.AddSingleton(serviceType,implementationType),
serviceType =>builder.Services.AddScoped(serviceType),Assembly.GetExecutingAssembly());varapp=builder.Build();EventBusExtensions.UseEventBus(
serviceType =>app.Services.GetRequiredService(serviceType),Assembly.GetExecutingAssembly());Subscribe(Assembly[]) 用于登记程序集中的 [Event] 实例处理器,但真正执行这些处理器时需要先有服务解析器。
所以通常应优先使用:
app.UseEventBus()container.UseEventBus()EventBusExtensions.UseEventBus(...)
如果没有 IOC 容器,请使用:
Subscribe(this)注册实例对象Subscribe<T>()扫描指定类型并注册处理器
同一个对象方法或同一个扫描命中的方法重复注册,不会重复执行。
Query<T> 的结果由处理器写入 Result。是否允许返回 null 由你的查询类型决定,例如:
publicsealedclassProductQuery:Query<ProductItemDto?>{publicoverrideProductItemDto?Result{get;set;}}[EventHandler(Order = n)] 可以控制同一命令下多个处理器的执行顺序,值越小越先执行。
publicinterfaceIEventBus{voidSubscribe<T>()whereT:class;voidSubscribe(Typetype);voidSubscribe(objectrecipient);voidSubscribe<TCommand>(Action<TCommand>action)whereTCommand:Command;voidSubscribe<TCommand>(Func<TCommand,Task>asyncAction)whereTCommand:Command;voidSubscribe(Assembly[]assemblies);voidUnsubscribe<T>()whereT:class;voidUnsubscribe(objectrecipient);voidUnsubscribe<TCommand>(Action<TCommand>action)whereTCommand:Command;voidUnsubscribe<TCommand>(Func<TCommand,Task>asyncAction)whereTCommand:Command;voidPublish<TCommand>(TCommandcommand)whereTCommand:Command;TResponseQuery<TResponse>(Query<TResponse>query);TaskPublishAsync<TCommand>(TCommandcommand)whereTCommand:Command;Task<TResponse>QueryAsync<TResponse>(Query<TResponse>query);voidRegisterServiceHandlerAction(Action<Type,Action<object>>serviceHandlerAction);}仓库内可直接参考:
在仓库根目录运行 pack.bat,脚本会执行 dotnet restore 和 Release 构建,并把生成的 NuGet 包输出到 Output\NuGet。
检查方式:NuGet 元数据、恢复后的 project.assets.json、NuGet.org 与源码仓库信息。优先接受 MIT / Apache-2.0 / BSD;其它开源协议在源码与传递依赖均可追溯时单独标注通过。
整改:
包版本提升到
3.4.5.5,用于发布本次依赖升级与审计修正。CodeWF.EventBus核心包已补充PackageLicenseExpression=MIT,核心包无第三方运行时依赖。CodeWF.DryIoc.EventBus将Prism.Core从9.0.537降到 MIT 的8.1.97,避开 Prism 9 的 Community/Commercial License。CodeWF.DryIoc.EventBus将DryIoc从6.0.0-preview-08改为稳定版5.4.3。Avalonia AOT 示例升级到 Avalonia
12.0.3、ReactiveUI.Avalonia 12.0.1、Semi.Avalonia 12.0.1、CodeWF.LogViewer.Avalonia 12.0.3.1。移除 Debug-only 的
Avalonia.Diagnostics引用;该包当前最新稳定版仍为11.3.16,没有 Avalonia 12 对应稳定包,且示例代码未使用诊断 API。Tmds.DBus.Protocol从 Avalonia 传递依赖0.92.0pin 到0.93.0。测试依赖升级到
Microsoft.NET.Test.Sdk 18.5.1、coverlet.collector 10.0.1;xunit.runner.visualstudio保持稳定3.1.5,不使用4.0.0-pre.4预览版。包 使用范围 协议 源码/项目地址 结论
DryIocDryIoc 扩展包 MIT https://github.com/dadhi/DryIoc 通过Prism.Core8.1.97DryIoc/Prism 扩展包 MIT https://github.com/PrismLibrary/Prism 通过,保留 8.x 开源线Swashbuckle.AspNetCoreWeb API 示例 MIT https://github.com/domaindrivendev/Swashbuckle.AspNetCore 通过Avalonia/Avalonia.Desktop/Avalonia.Fonts.Inter12.0.3AOT 示例 MIT https://github.com/AvaloniaUI/Avalonia 通过ReactiveUI.Avalonia12.0.1AOT 示例 MIT https://github.com/reactiveui/reactiveui 通过,使用匹配 Avalonia 12 的包线Semi.Avalonia12.0.1AOT 示例 MIT https://github.com/irihitech/Semi.Avalonia 通过,仅使用开源主体包CodeWF.Log.Core/CodeWF.LogViewer.Avalonia12.0.3.1AOT 示例日志 MIT https://github.com/dotnet9/CodeWF.LogViewer 自研开源包CodeWF.Tools.Core1.3.13日志组件传递依赖 pin MIT https://github.com/dotnet9/CodeWF.Tools 自研开源包Tmds.DBus.Protocol0.93.0Avalonia Linux DBus 传递依赖 MIT https://github.com/tmds/Tmds.DBus 通过,pin 到当前稳定版Microsoft.NET.Test.Sdk18.5.1测试 MIT https://github.com/microsoft/vstest 通过coverlet.collector10.0.1测试覆盖率 MIT https://github.com/coverlet-coverage/coverlet 通过xunit/xunit.runner.visualstudio测试 Apache-2.0 https://github.com/xunit/xunit 通过
传递依赖检查结论:恢复后的有效依赖链未发现黑盒包、Prism 9 商业协议包、AvaloniaUI.DiagnosticsSupport、Semi.Avalonia.* 黑盒扩展或预发布 DryIoc 运行时依赖。DryIoc 5.4.3 构建时会在包内源码触发 SYSLIB0051 过时 API 警告,但不是许可证或已知漏洞告警。
XML 文件统一使用两个空格缩进。Directory.Packages.props 统一承载 NuGet 中央包管理开关和包版本变量,包括 AvaloniaVersion 等共享版本属性;Directory.Build.props 仅保留项目构建、编译选项和 NuGet 元数据。仓库如引用 VC-LTL、YY-Thunks,这两个兼容旧版操作系统的特殊包必须使用最新预览版。