{"id":23062207,"url":"https://github.com/dotnet9/codewf.eventbus","last_synced_at":"2025-04-06T07:09:55.989Z","repository":{"id":243404421,"uuid":"812332698","full_name":"dotnet9/CodeWF.EventBus","owner":"dotnet9","description":"事件总线(EventBus)是一种解耦模块间通讯的强大工具。在CodeWF.EventBus库中，我们得以轻松实现CQRS模式，并通过清晰、简洁的接口进行事件订阅与发布。","archived":false,"fork":false,"pushed_at":"2025-03-21T03:07:35.000Z","size":225,"stargazers_count":45,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-30T06:05:01.399Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://codewf.com","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dotnet9.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-08T15:36:11.000Z","updated_at":"2025-03-22T14:01:39.000Z","dependencies_parsed_at":"2024-06-20T16:41:14.848Z","dependency_job_id":"5c11fbf6-5993-488e-9046-4aaf799bbb0e","html_url":"https://github.com/dotnet9/CodeWF.EventBus","commit_stats":null,"previous_names":["dotnet9/codewf.eventbus"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotnet9%2FCodeWF.EventBus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotnet9%2FCodeWF.EventBus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotnet9%2FCodeWF.EventBus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dotnet9%2FCodeWF.EventBus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dotnet9","download_url":"https://codeload.github.com/dotnet9/CodeWF.EventBus/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247445667,"owners_count":20939958,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-16T03:25:04.503Z","updated_at":"2025-04-06T07:09:55.932Z","avatar_url":"https://github.com/dotnet9.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CodeWF.EventBus\n\n## 1. 前言\n\n事件总线，即EventBus，是一种解耦模块间通讯的强大工具。在 [CodeWF.EventBus](https://github.com/dotnet9/CodeWF.EventBus) 中，我们得以轻松实现CQRS模式，并通过清晰、简洁的接口进行事件订阅与发布。接下来，我们将详细探讨如何使用这个库来处理事件。\n\n\u003e CQRS，全称Command Query Responsibility Segregation，是一种软件架构模式，旨在通过将系统中的命令（写操作）和查询（读操作）职责进行分离，来提高系统的性能、可伸缩性和响应性。\n\n[CodeWF.EventBus](https://github.com/dotnet9/CodeWF.EventBus) 适用于进程内事件传递（无其他外部依赖），与 [MediatR](https://github.com/jbogard/MediatR) 功能类似。[MediatR](https://github.com/jbogard/MediatR)库侧重于[ASP.NET Core](https://learn.microsoft.com/zh-cn/aspnet/core/?view=aspnetcore-9.0)设计，且其功能更加强大，[CodeWF.EventBus](https://github.com/dotnet9/CodeWF.EventBus)库优势：\n\n1. 小巧灵活，设计可在各种模板项目使用，如 WPF、Winform、Avalonia UI、ASP.NET Core 等。\n2. 支持使用了任何 `IOC` 容器的项目。\n3. 参考[MASA Framework](https://docs.masastack.com/framework/tutorial/mf-part-3#section-69828ff0)增强事件处理能力，支持一个类定义多个事件处理方法：\n\n## 2. 使用说明\n\n### 2.1. 注册事件总线\n\n#### 2.1.1. MS.DI容器\n\n主要是ASP.NET Core程序，比如 MVC、Razor Pages、Blazor Server 等模板程序，请搜索 NuGet 包`CodeWF.AspNetCore.EventBus`并安装最新版，安装完成后，在`Program`中添加如下代码：\n\n```csharp\n// ....\n\n// 1、注册事件总线，将标注`EventHandler`特性方法的类采用单例方式注入IOC容器\nbuilder.Services.AddEventBus();\n\nvar app = builder.Build();\n\n// ...\n\n// 2、将上面已经注入IOC容器的类取出、关联处理方法到事件总线管理\napp.UseEventBus();\n\n// ...\n```\n\n- `AddEventBus`方法会扫描传入的程序集列表，将标注`Event`特性的类下又标注`EventHandler`特性方法的类采用单例方式注入 IOC 容器。\n- `UseEventBus`方法会将上一步注入的类通过 IOC 容器获取到实例，将实例的事件处理方法注册到事件管理队列中去，待收到事件发布时，会从事件管理队列中查找事件处理方法并调用，达到事件通知的功能。\n\n#### 2.1.2. DryIOC容器\n\n如果使用的`DryIoc`容器，比如 WPF /Avalonia UI中使用了 Prism 框架的DryIoc容器，请搜索 NuGet 包`CodeWF.DryIoc.EventBus`并安装最新版，安装完成后，在`RegisterTypes`方法中添加如下代码：\n\n```csharp\nprotected override void RegisterTypes(IContainerRegistry containerRegistry)\n{\n    IContainer? container = containerRegistry.GetContainer();\n\n    // ...\n\n    // Register EventBus\n    containerRegistry.AddEventBus();\n\n    // ...\n\n    // Use EventBus\n    container.UseEventBus();\n}\n```\n\n#### 2.1.3. 任意IOC容器\n\n如果使用了其他`IOC`容器的项目，请搜索 NuGet 包`CodeWF.IOC.EventBus`并安装最新版，安装完成后，根据 IOC 容器注册单例、获取服务的 API 不同，做相应修改即可。\n\n上面`ASP.NET Core`示例注册事件总线可改为：\n\n```csharp\n// ....\n\n// 1、注册事件总线，将标注`EventHandler`特性方法的类采用单例方式注入IOC容器\nEventBusExtensions.AddEventBus(\n    (t1, t2) =\u003e builder.Services.AddSingleton(t1, t2),\n    t =\u003e builder.Services.AddSingleton(t),\n    Assembly.GetExecutingAssembly());\n\nvar app = builder.Build();\n\n// ...\n\n// 2、将上面已经注入IOC容器的类取出、关联处理方法到事件总线管理\nEventBusExtensions.UseEventBus(t =\u003e app.Services.GetRequiredService(t), Assembly.GetExecutingAssembly());\n\n// ...\n```\n\n支持任意`IOC`容器原理就在`AddEventBus`和`UseEventBus`方法：\n\n```csharp\nusing CodeWF.EventBus;\nusing System.Reflection;\n\nnamespace CodeWF.IOC.EventBus\n{\n    public static class EventBusExtensions\n    {\n        public static void AddEventBus(Action\u003cType, Type\u003e addSingleton1,\n            Action\u003cType\u003e addSingleton2, params Assembly[] assemblies)\n        {\n            addSingleton1(typeof(IEventBus), typeof(CodeWF.EventBus.EventBus));\n\n            var allAssemblies = assemblies.Concat(new[] { Assembly.GetCallingAssembly() }).ToArray();\n\n            CodeWF.EventBus.EventBusExtensions.HandleEventObject(type =\u003e addSingleton2(type),\n                BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,\n                allAssemblies);\n        }\n\n        public static void UseEventBus(Func\u003cType, object\u003e resolveAction, params Assembly[] assemblies)\n        {\n            if (!(resolveAction(typeof(IEventBus)) is IEventBus messenger))\n            {\n                throw new InvalidOperationException(\"Please call AddEventBus before calling UseEventBus\");\n            }\n\n            var allAssemblies = assemblies.Concat(new[] { Assembly.GetCallingAssembly() }).ToArray();\n\n            CodeWF.EventBus.EventBusExtensions.HandleEventObject(\n                type =\u003e messenger.Subscribe(resolveAction(type)),\n                BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, allAssemblies);\n\n            CodeWF.EventBus.EventBusExtensions.HandleEventObject(type =\u003e messenger.Subscribe(type),\n                BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, allAssemblies);\n        }\n    }\n}\n```\n\n```csharp\nusing System;\nusing System.Linq;\nusing System.Reflection;\n\nnamespace CodeWF.EventBus\n{\n    public static class EventBusExtensions\n    {\n        public static void HandleEventObject(Action\u003cType\u003e handleRecipient, BindingFlags findHandlerMethodBindingFlags,\n            Assembly[] assemblies)\n        {\n            foreach (var assembly in assemblies)\n            {\n                var types = assembly.GetTypes()\n                    .Where(t =\u003e t.IsClass\n                                \u0026\u0026 !t.IsAbstract\n                                \u0026\u0026 t.GetCustomAttributes\u003cEventAttribute\u003e().Any()\n                                \u0026\u0026 t.GetMethods(findHandlerMethodBindingFlags)\n                                    .Any(m =\u003e\n                                        m.GetCustomAttributes\u003cEventHandlerAttribute\u003e().Any()));\n\n                foreach (var type in types)\n                {\n                    handleRecipient(type);\n                }\n            }\n        }\n    }\n}\n```\n\n#### 2.1.4. 未使用任何 IOC容器\n\n默认的 WPF、Winform、Avalonia UI、控制台程序默认未引入任何 IOC 容器，这类项目我们可以不需要事件服务注册操作。\n\n我们搜索 NuGet 包`CodeWF.EventBus`并安装最新版，安装完成后功能使用上和使用`IOC`容器一致，只是欠缺`IOC`注入自动订阅功能，具体差别请继续往下看。\n\n### 2.2. 定义事件\n\n在这里我们使用 CQRS 来完成我们程序业务逻辑，在 CQRS 模式中我们的查询和其它业务操作是分开的。不了解 CQRS 的可以看看这篇文章：https://learn.microsoft.com/zh-cn/azure/architecture/patterns/cqrs\n\n#### 2.2.1. 定义命令(Command)\n\n在CQRS模式中，命令代表写操作。定义命令类，这些类继承自`Command`类\n\n```CSharp\npublic class CreateProductCommand : Command\n{\n    public string Name { get; set; }\n    public decimal Price { get; set; }\n}\npublic class CreateProductSuccessCommand : Command\n{\n    public string Name { get; set; }\n    public decimal Price { get; set; }\n}\npublic class DeleteProductCommand : Command\n{\n    public Guid ProductId { get; set; }\n}\n```\n\n#### 2.2.2. 定义查询(Query)\n\n在CQRS模式中，查询代表读操作。查询需要等待得到回应，适用于请求/响应。使用查询，调用方只需要关心我需要使用`ProductQuery`、`ProductsQuery`，而不必操心我需要`IProductService`、`ICategoryService`等服务获取查询结果。\n\n定义查询类，继承自`Query\u003cT\u003e`:\n\n```csharp\npublic class ProductQuery : Query\u003cProductItemDto\u003e\n{\n    public Guid ProductId { get; set; }\n    public override ProductItemDto Result { get; set; }\n}\npublic class ProductsQuery : Query\u003cList\u003cProductItemDto\u003e\u003e\n{\n    public string Name { get; set; }\n    public override List\u003cProductItemDto\u003e Result { get; set; }\n}\n```\n\n`Query\u003cT\u003e`中T表示查询响应结果类型，在`XXXQuery`中使用`Result`属性表示查询发布后得到的结果。\n\n`Query`继承自`Command`，带`Result`属性：\n\n```csharp\npublic abstract class Query\u003cTResult\u003e : Command\n{\n    public abstract TResult Result { get; set; }\n}\n```\n\n### 2.3. 订阅事件（Subscribe）\n\n#### 2.3.1. 自动订阅\n\n`自动订阅`只能在使用了`IOC`容器的程序中使用，比如`ASP.NET Core`程序。\n\n一般将事件处理程序单独封装到一个类中，代码如下：\n\n```csharp\n[Event]\npublic class CommandAndQueryHandler(IEventBus eventBus, IProductService productService)\n{\n    [EventHandler]\n    private async Task ReceiveCreateProductCommandAsync(CreateProductCommand command)\n    {\n        var isAddSuccess = await productService.AddProductAsync(new CreateProductRequest()\n            { Name = command.Name, Price = command.Price });\n        if (isAddSuccess)\n        {\n            await eventBus.PublishAsync(new CreateProductSuccessCommand()\n                { Name = command.Name, Price = command.Price });\n        }\n        else\n        {\n            Console.WriteLine(\"Create product fail\");\n        }\n    }\n\n    [EventHandler(Order = 2)]\n    private async Task ReceiveCreateProductSuccessCommandSendEmailAsync(CreateProductSuccessCommand command)\n    {\n        Console.WriteLine($\"Now send email notify create product success, name is = {command.Name}\");\n        await Task.CompletedTask;\n    }\n\n    [EventHandler(Order = 1)]\n    private async Task ReceiveCreateProductSuccessCommandSendSmsAsync(CreateProductSuccessCommand command)\n    {\n        Console.WriteLine($\"Now send sms notify create product success, name is = {command.Name}\");\n        await Task.CompletedTask;\n    }\n\n    [EventHandler(Order = 3)]\n    private void ReceiveCreateProductSuccessCommandCallPhone(CreateProductSuccessCommand command)\n    {\n        Console.WriteLine($\"Now call phone notify create product success, name is = {command.Name}\");\n    }\n\n    [EventHandler]\n    private async Task ReceiveDeleteProductCommandAsync(DeleteProductCommand command)\n    {\n        var isRemoveSuccess = await productService.RemoveProductAsync(command.ProductId);\n        Console.WriteLine(isRemoveSuccess ? \"Remote product success\" : \"Remote product fail\");\n    }\n\n    [EventHandler]\n    private async Task ReceiveProductQueryAsync(ProductQuery query)\n    {\n        var product = await productService.QueryProductAsync(query.ProductId);\n        query.Result = product;\n    }\n\n    [EventHandler]\n    private async Task ReceiveAutoProductsQueryAsync(ProductsQuery query)\n    {\n        var products = await productService.QueryProductsAsync(query.Name);\n        query.Result = products;\n    }\n\n    [EventHandler]\n    private static async Task ReceiveAutoProductsQueryAsync2(ProductsQuery query)\n    {\n        Console.WriteLine(\"Test auto subscribe static method\");\n    }\n}\n```\n\n- 类`CommandAndQueryHandler`添加了`Event`特性，在 `IOC` 容器注入时标识为可以做为单例注入。\n- 标注了`EventHandler`特性的方法拥有处理事件的能力，该方法只能有一个事件类型参数；如果方法支持异步，也只支持`Task`返回值，不能加泛型声明（加了无效）；支持静态事件处理方法。\n\n使用 IOC 容器的程序会自动将标注`Event`特性的类做为单例注入容器，事件总线收到事件通知时自动查找标注`EventHandler`特性的方法进行调用，达到事件通知的功能。\n\n#### 2.3.2. 手动订阅\n\n对于未标注`Event`特性的类，可手动注册事件处理程序，如下是未使用 `IOC`容器时手动注册示例（核心是`EventBus.Default`使用）：\n\n```csharp\ninternal class CommandAndQueryHandler\n{\n    internal void ManuSubscribe()\n    {\n        EventBus.Default.Subscribe\u003cDeleteProductCommand\u003e(ReceiveDeleteProductCommandAsync);\n        EventBus.Default.Subscribe\u003cProductQuery\u003e(ReceiveProductQueryAsync);\n        EventBus.Default.Subscribe\u003cProductsQuery\u003e(ReceiveAutoProductsQueryAsync2);\n    }\n\n    public async Task ReceiveDeleteProductCommandAsync(DeleteProductCommand command)\n    {\n    }\n\n    public async Task ReceiveProductQueryAsync(ProductQuery query)\n    {\n    }\n\n    private static async Task ReceiveAutoProductsQueryAsync2(ProductsQuery query)\n    {\n    }\n}\n```\n\n上面挨个注册处理方法有时会过于啰嗦，可以简化：\n\n```csharp\ninternal class CommandAndQueryHandler\n{\n    internal CommandAndQueryHandler()\n    {\n        EventBus.Default.Subscribe(this);\n    }\n\n    [EventHandler(Order = 2)]\n    public async Task ReceiveCreateProductSuccessCommandSendEmailAsync(CreateProductSuccessCommand command)\n    {\n    }\n\n    // ...省略N多事件处理方法，EventBus.Default.Subscribe(this)方法可以自动绑定\n}\n```\n\n使用了 `IOC`容器，可以注入`IEventBus`服务替换`EventBus.Default`使用，下如示例代码：\n\n```csharp\npublic class EventBusTestViewModel : ViewModelBase\n{\n    private readonly IEventBus _eventBus;\n\n    public MessageTestViewModel(IEventBus eventBus)\n    {\n        _eventBus = eventBus;\n        _eventBus.Subscribe(this);\n    }\n    \n    [EventHandler]\n    public async Task ReceiveDeleteProductCommandAsync(DeleteProductCommand command)\n    {\n        var isRemoveSuccess = await productService.RemoveProductAsync(command.ProductId);\n        Console.WriteLine(isRemoveSuccess ? \"Remote product success\" : \"Remote product fail\");\n    }\n}\n```\n\n`EventBus`是`IEventBus`接口的默认实现，`EventBus.Default`是单例引用，所有两者使用任选其一。`IOC`注入时默认将`IEventBus`和`EventBus`做为单例注入，所以与两者等价。\n\n手动订阅可以在 WPF 的 `XxxViewModel` 中使用（上面代码即是），也可以在 `IOC` 其他生命周期的服务中使用：\n\n```csharp\npublic class TimeService : ITimeService\n{\n    private readonly IEventBus _eventBus;\n\n    public TimeService(IEventBus eventBus)\n    {\n        _eventBus = eventBus;\n        _eventBus.Subscribe(this);\n    }\n    \n\t[EventHandler]\n    public async Task ReceiveDeleteProductCommandAsync(DeleteProductCommand command)\n    {\n        var isRemoveSuccess = await productService.RemoveProductAsync(command.ProductId);\n        Console.WriteLine(isRemoveSuccess ? \"Remote product success\" : \"Remote product fail\");\n    }\n}\n```\n\n手动注册可运用在无法或不需要单例注入的情况使用，补充特殊情况。\n\n### 2.4. 发布事件\n\n发布命令(Command)与发布查询(Query)使用相同的接口，通过`IEventBus`或`EventBus.Default`的`Publish`和`PublishAsync`方法发布：\n\n```csharp\n_messenger.Publish(this, new DeleteProductCommand { ProductId = id });\n```\n\n```csharp\nvar query = new ProductQuery { ProductId = id };\nawait _messenger.PublishAsync(this, query);\nConsole.WriteLine($\"查询产品ID为{id}的产品结果是：{query.Result}\");\n```\n\n在`B/S`控制器的`Action`使用发布：\n\n```csharp\n[ApiController]\n[Route(\"[controller]\")]\npublic class EventController : ControllerBase\n{\n    private readonly ILogger\u003cEventController\u003e _logger;\n    private readonly IEventBus _eventBus;\n\n    public EventController(ILogger\u003cEventController\u003e logger, IEventBus eventBus)\n    {\n        _logger = logger;\n        _eventBus = eventBus;\n    }\n\n    [HttpPost(\"/add\")]\n    public async Task AddAsync([FromBody] CreateProductRequest request)\n    {\n        await _eventBus.PublishAsync(new CreateProductCommand { Name = request.Name, Price = request.Price });\n    }\n\n    [HttpDelete(\"/delete\")]\n    public async Task DeleteAsync([FromQuery] Guid id)\n    {\n        await _eventBus.PublishAsync(new DeleteProductCommand { ProductId = id });\n    }\n\n    [HttpGet(\"/get\")]\n    public async Task\u003cProductItemDto\u003e GetAsync([FromQuery] Guid id)\n    {\n        var query = new ProductQuery { ProductId = id };\n        await _eventBus.PublishAsync(query);\n        return query.Result;\n    }\n\n    [HttpGet(\"/list\")]\n    public async Task\u003cList\u003cProductItemDto\u003e\u003e ListAsync([FromQuery] string? name)\n    {\n        var query = new ProductsQuery { Name = name };\n        await _eventBus.PublishAsync(query);\n        return query.Result;\n    }\n}\n```\n\n在`WPF/Avalonia UI`的`XXXViewModel`中使用:\n\n```csharp\npublic class EventBusTestViewModel : ViewModelBase\n{\n    private readonly IEventBus _eventBus;\n\n    public MessageTestViewModel(IEventBus eventBus)\n    {\n        _eventBus = eventBus;\n    }\n\n    public async Task ExecuteEventBusAsync()\n    {\n        await _eventBus.PublishAsync(this, new TestMessage(nameof(MessageTestViewModel), TestClass.CurrentTime()));\n    }\n}\n```\n\n### 2.5. 取消订阅事件\n\n在实际应用中，你可能需要确保在适当的时机（如服务销毁时）取消订阅，以避免内存泄漏：\n\n1. 注销指定处理程序：`Messenger.Default.Unsubscribe\u003cCreateProductMessage\u003e(this, ReceiveManuCreateProductMessage)`\n2. 注销指定类的所有处理程序：`Messenger.Default.Unsubscribe(this)`\n\n## 3. 核心接口说明\n\n```csharp\npublic interface IEventBus\n{\n    void Subscribe\u003cT\u003e() where T : class;\n    void Subscribe(Type type);\n    void Subscribe(object recipient);\n    void Subscribe\u003cTCommand\u003e(Action\u003cTCommand\u003e action) where TCommand : Command;\n    void Subscribe\u003cTCommand\u003e(Func\u003cTCommand, Task\u003e asyncAction) where TCommand : Command;\n    void Unsubscribe\u003cT\u003e() where T : class;\n    void Unsubscribe(object recipient);\n    void Unsubscribe\u003cTCommand\u003e(Action\u003cTCommand\u003e action) where TCommand : Command;\n    void Unsubscribe\u003cTCommand\u003e(Func\u003cTCommand, Task\u003e asyncAction) where TCommand : Command;\n    void Publish\u003cTCommand\u003e(TCommand command) where TCommand : Command;\n    Task PublishAsync\u003cTCommand\u003e(TCommand command) where TCommand : Command;\n}\n```\n\n- `Subscribe\u003cT\u003e()`：订阅类中静态事件处理方法\n- `Subscribe(Type type)`：订阅指定类类型中静态事件处理方法\n- `Subscribe(object recipient)`：订阅指定实例的成员事件处理方法\n- `Subscribe\u003cTCommand\u003e(Action\u003cTCommand\u003e action)`：订阅普通事件处理方法，包括静态事件处理方法\n- `Subscribe\u003cTCommand\u003e(Func\u003cTCommand, Task\u003e asyncAction)`：订阅异步事件处理方法，包括静态异步事件处理方法\n- `Unsubscribe\u003cT\u003e()`：注销类中静态事件处理方法\n- `Unsubscribe(object recipient)`：注销指定实例的成员事件处理方法\n- `Unsubscribe\u003cTCommand\u003e(Action\u003cTCommand\u003e action)`：注销普通事件处理方法，包括静态事件处理方法\n- `Unsubscribe\u003cTCommand\u003e(Func\u003cTCommand, Task\u003e asyncAction)`：注销异步事件处理方法，包括静态异步事件处理方法\n- `Publish\u003cTCommand\u003e(TCommand command)`：同步发布命令(Command)或查询(Query)\n- `PublishAsync\u003cTCommand\u003e(TCommand command)`：异步发布命令(Command)或查询(Query)\n\n## 4. 总结\n\n`CodeWF.EventBus`提供了一个小巧灵活的事件总线实现，支持CQRS模式，并适用于各种项目模板，如 Avalonia UI、WPF、WinForms、ASP.NET Core 等。通过简单的订阅和发布操作，你可以轻松实现模块间的解耦和通讯。通过有序的事件处理，确保事件得到妥善处理。\n\n事件总线具体实现请查看`CodeWF.EventBus`源码： https://github.com/dotnet9/CodeWF.EventBus ，具体使用可参考：\n\n1. 单元测试：[CodeWF.EventBus.Tests](https://github.com/dotnet9/CodeWF.EventBus/tree/main/src/CodeWF.EventBus.Tests)\n3. AvaloniaUI + Prism：[CodeWF.Toolbox](https://github.com/dotnet9/CodeWF.Toolbox/)\n4. Web API：[WebAPIDemo](https://github.com/dotnet9/CodeWF.EventBus/tree/main/src/WebAPIDemo) 、[CodeWF](https://github.com/dotnet9/CodeWF)\n\n开发参考开源项目：\n\n1. [Messenger | MvvmCross](https://www.mvvmcross.com/documentation/plugins/messenger?scroll=1000)\n2. [Prism.Events](https://github.com/PrismLibrary/Prism/tree/master/src/Prism.Events)\n3. [MediatR](https://github.com/jbogard/MediatR)\n4. [MASA Framework](https://docs.masastack.com/framework/tutorial/mf-part-3#section-69828ff0)\n\n希望本文的指南能帮助你更好地使用`CodeWF.EventBus`来处理你的应用程序中的事件。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdotnet9%2Fcodewf.eventbus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdotnet9%2Fcodewf.eventbus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdotnet9%2Fcodewf.eventbus/lists"}