支持所有IOC的事件总线-CodeWF.EventBus

科技   2024-06-19 08:03   北京  

本次更新:删除CodeWF.Asp.NetCore.EventBus包,CodeWF.EventBus包支持所有IOC注入,不依赖ASP.NET Core及其他依赖。

1. 简介

EventBus(事件总线),用于解耦模块之间的通讯。本库(CodeWF.EventBus)适用于进程内消息传递(无其他外部依赖),与大家普遍使用的MediatR通知功能类似,但MediatR库侧重于ASP.NET Core设计使用,本库优势:

  1. 设计可在各种模板项目使用,如 WPF、Winform、Avalonia UI、ASP.NET Core 等。

  2. 支持使用了 IOC 容器的项目,当然也支持未使用任何 IOC 容器的模板项目。

  3. 参考MASA Framework增强消息处理能力:

 1[Event]
2public class MessageHandler
3{
4    private readonly ITimeService timeService;
5
6    public MessageHandler(ITimeService timeService)
7    
{
8        this.timeService = timeService;
9    }
10
11    [EventHandler(Order = 3)]
12    public void ReceiveAutoCreateProductMessage3(CreateProductMessage message)
13    
{
14        AddLog($"MessageHandler Received message 3 \"{message}\"");
15    }
16
17    [EventHandler(Order = 1)]
18    public void ReceiveAutoDeleteProductMessage(DeleteProductMessage message)
19    
{
20        AddLog($"MessageHandler Received message \"{message}\"");
21    }
22
23    [EventHandler(Order = 2)]
24    public void ReceiveAutoCreateProductMessage2(CreateProductMessage message)
25    
{
26        AddLog($"MessageHandler Received message 2 \"{message}\"");
27    }
28
29    private void AddLog(string message)
30    
{
31        Console.WriteLine($"{timeService.GetTime()}{message}\r\n");
32    }
33}

2. 怎么使用事件总线?

首先请搜索 NuGet 包CodeWF.EventBus并安装,下面细说使用方法。

2.1. 添加事件总线

2.1.1. 使用了 IOC

如果是 ASP.NET Core 程序,比如 MVC、Razor Pages、Blazor Server 等,,在Program中添加如下代码:

 1// ....
2
3// 1、注册事件总线,将标注`EventHandler`特性方法的类采用单例方式注入IOC容器
4EventBusExtensions.AddEventBus(
5    (t1, t2) => builder.Services.AddSingleton(t1, t2),
6    t => builder.Services.AddSingleton(t),
7    typeof(Program).Assembly);
8
9var app = builder.Build();
10
11// ...
12
13// 2、将上面已经注入IOC容器的类取出、关联处理方法到事件总线管理
14EventBusExtensions.UseEventBus((t) => app.Services.GetRequiredService(t), typeof(Program).Assembly);
15
16// ...
  • AddEventBus方法会扫描传入的程序集列表,将标注Event特性的类下又标注EventHandler特性方法的类采用单例方式注入 IOC 容器。

  • UseEventBus方法会将上一步注入的类通过 IOC 单例获取到实例,将实例的消息处理方法注册到消息管理队列中去,待收到消息发布时,会从消息管理队列中查找消息处理方法并调用,达到消息通知的功能。

如果使用的其他 IOC,比如 WPF 中使用了 Prism 框架,写法如下:

 1protected override void RegisterTypes(IContainerRegistry containerRegistry)
2
{
3    IContainer? container = containerRegistry.GetContainer();
4
5    // ...
6
7    // Register EventBus
8    EventBusExtensions.AddEventBus(
9        (t1,t2)=> containerRegistry.RegisterSingleton(t1,t2),
10        t=> containerRegistry.RegisterSingleton(t),
11        typeof(App).Assembly);
12
13    // ...
14
15    // Use EventBus
16    EventBusExtensions.UseEventBus(t => container.Resolve(t), typeof(App).Assembly);
17}

根据 IOC 注册单例、获取服务的 API 不同,做相应修改即可。

2.1.2. 未使用 IOC

未使用 IOC 容器,比如默认的 WPF、Winform、AvaloniaUI、控制台程序不包含 IOC 容器,不做事件服务注册操作。

2.2. 定义消息(事件类型)

首先定义消息类,即需要发布或订阅的事件类型,消息需要继承自CodeWF.EventBus.Message

 1public class CreateProductMessage : CodeWF.EventBus.Message
2{
3    public string Name { get; }
4
5    public CreateProductMessage(object sender, string name) : base(sender)
6    
{
7        Name = name;
8    }
9
10    public override string ToString()
11    
{
12        return $"Create product message ->Product name:{Name}";
13    }
14}
15
16public class DeleteProductMessage : CodeWF.EventBus.Message
17{
18    public string Id { get; }
19
20    public DeleteProductMessage(object sender, string id) : base(sender)
21    
{
22        Id = id;
23    }
24
25    public override string ToString()
26    
{
27        return $"Delete product message ->Product ID:{Id}";
28    }
29}

2.3. 订阅消息(事件)

2.3.1. 自动订阅

B/S程序中,一般将事件处理程序单独封装到一个类中,如下代码:

 1[Event]
2public class MessageHandler
3{
4    private readonly ITimeService timeService;
5
6    public MessageHandler(ITimeService timeService)
7    
{
8        this.timeService = timeService;
9    }
10
11    [EventHandler(Order = 3)]
12    public void ReceiveAutoCreateProductMessage3(CreateProductMessage message)
13    
{
14        AddLog($"MessageHandler Received message 3 \"{message}\"");
15    }
16
17    [EventHandler(Order = 1)]
18    public void ReceiveAutoDeleteProductMessage(DeleteProductMessage message)
19    
{
20        AddLog($"MessageHandler Received message \"{message}\"");
21    }
22
23    [EventHandler(Order = 2)]
24    public void ReceiveAutoCreateProductMessage2(CreateProductMessage message)
25    
{
26        AddLog($"MessageHandler Received message 2 \"{message}\"");
27    }
28}
  • MessageHandler添加了Event特性,在 IOC 注入时标识为可以做为单例注入。

  • 标注了EventHandler特性的方法拥有处理消息的能力,该方法只能有一个事件类型参数。

使用 IOC 容器的程序会自动将标注Event特性的类做为单例注入容器,事件总线收到消息通知时自动查找标注EventHandle特性的方法进行调用,达到消息通知的功能。

2.3.2. 手动订阅

对于未标注Event特性的类,可手动注册消息处理程序,如下图是未使用 IOC,手动注册示例:

 1internal class MessageHandler
2{
3    internal void ManuSubscribe()
4    
{
5        Messenger.Default.Subscribe<CreateProductMessage>(this, ReceiveManuCreateProductMessage);
6        Messenger.Default.Subscribe<DeleteProductMessage>(this, ReceiveManuDeleteProductMessage);
7    }
8
9    void ReceiveManuCreateProductMessage(CreateProductMessage message)
10    
{
11        AddLog($"Received manually registered \"{message}\"");
12    }
13
14    void ReceiveManuDeleteProductMessage(DeleteProductMessage message)
15    
{
16        AddLog($"Received manually registered \"{message}\"");
17    }
18}

上面挨个注册处理方法有时会过于啰嗦,可以简化:

 1internal class MessageHandler
2{
3    internal void AutoSubscribe()
4    
{
5        Messenger.Default.Subscribe(this);
6    }
7
8    [EventHandler(Order = 3)]
9    private void ReceiveAutoCreateProductMessage3(CreateProductMessage message)
10    
{
11        AddLog($"Received automatic subscription message 3 \"({message}\"");
12    }
13
14    [EventHandler(Order = 1)]
15    private void ReceiveAutoDeleteProductMessage(DeleteProductMessage message)
16    
{
17        AddLog($"Received automatic subscription message \"{message}\"");
18    }
19
20    [EventHandler(Order = 2)]
21    private void ReceiveAutoCreateProductMessage2(CreateProductMessage message)
22    
{
23        AddLog($"Received automatic subscription message 2 \"{message}\"");
24    }
25}

使用了 IOC,可以注入IMessenger服务替换Messenger.Default使用,MessengerIMessenger接口的默认实现,Messenger.Default是单例引用。

 1public class MessageTestViewModel : ViewModelBase
2{
3    private readonly IMessenger _messenger;
4
5    public MessageTestViewModel(IMessenger messenger)
6    
{
7        _messenger = messenger;
8        _messenger.Subscribe(this);
9    }
10
11    [EventHandler]
12    public void ReceiveEventBusMessage(TestMessage message)
13    
{
14        _notificationService?.Show("CodeWF EventBus",
15            $"模块【Test】收到{nameof(TestMessage)},Name: {message.Name}, Time: {message.CurrentTime}");
16    }
17}

手动订阅可以在 WPF 的 ViewModel 中使用(代码如上),也可以在 IOC 其他生命周期的服务中使用:

 1public class TimeService : ITimeService
2{
3    private readonly IMessenger _messenger;
4
5    public TimeService(IMessenger messenger)
6    
{
7        _messenger = messenger;
8
9        _messenger.Subscribe(this);
10    }
11
12
13    [EventHandler]
14    public void ReceiveEventBusMessage(TestMessage message)
15    
{
16    }
17}

手动注册可运用在无法或不需要单例注入的情况使用。

2.4. 发布消息

发布就比较简单:

1_messenger.Publish(thisnew TestMessage(thisnameof(MessageTestViewModel), TestClass.CurrentTime()));

比如在B/S控制器的Action使用:

 1[ApiController]
2[Route("[controller]")]
3public class EventController : ControllerBase
4{
5    private readonly ILogger<EventController> _logger;
6    private readonly IMessenger _messenger;
7
8    public EventController(ILogger<EventController> logger, IMessenger messenger)
9    
{
10        _logger = logger;
11        _messenger = messenger;
12    }
13
14    [HttpPost]
15    public void Add()
16    
{
17        _messenger.Publish(thisnew CreateProductMessage(this$"{DateTime.Now:HHmmss}号产品"));
18    }
19
20    [HttpDelete]
21    public void Delete()
22    
{
23        _messenger.Publish(thisnew DeleteProductMessage(this$"{DateTime.Now:HHmmss}号"));
24    }
25}

WPF/Avalonia UIViewModel中使用:

 1public class MessageTestViewModel : ViewModelBase
2{
3    private readonly IMessenger _messenger;
4
5    public MessageTestViewModel(IMessenger messenger)
6    
{
7        _messenger = messenger;
8    }
9
10    public Task ExecuteEventBusAsync()
11    
{
12        _messenger.Publish(thisnew TestMessage(thisnameof(MessageTestViewModel), TestClass.CurrentTime()));
13        return Task.CompletedTask;
14    }
15}

2.5. 取消订阅消息(事件)

支持消息处理程序的注销:

  1. 注销指定处理程序:Messenger.Default.Unsubscribe(this, ReceiveManuCreateProductMessage)

  2. 注销指定类的所有处理程序:Messenger.Default.Unsubscribe(this)

3. 总结

CodeWF.EventBus,一款灵活的事件总线库,实现模块间解耦通信。支持多种.NET 项目类型,如 Avalonia UI、WPF、WinForms、ASP.NET Core 等。采用简洁设计,轻松实现事件的发布与订阅。通过有序的消息处理,确保事件得到妥善处理。

简化您的代码,提升系统可维护性。

立即体验 CodeWF.EventBus,让事件处理更加高效!

仓库地址 https://github.com/dotnet9/CodeWF.EventBus,具体使用可参考Demo如下:

  1. ConsoleDemo:https://github.com/dotnet9/CodeWF.EventBus/tree/main/src/ConsoleDemo

  2. WPFDemo:https://github.com/dotnet9/CodeWF.EventBus/tree/main/src/WPFDemo

  3. AvaloniaUI + Prism:https://github.com/dotnet9/Tools.CodeWF/tree/prism-modules

  4. WebAPIDemo:https://github.com/dotnet9/CodeWF.EventBus/tree/main/src/WebAPIDemo

开发过程中参考不少开源项目,他们是:

  1. Messenger | MvvmCross:https://www.mvvmcross.com/documentation/plugins/messenger?scroll=1000

  2. Prism.Events:https://github.com/PrismLibrary/Prism/tree/master/src/Prism.Events

  3. MediatR:https://github.com/jbogard/MediatR

  4. MASA Framework:https://docs.masastack.com/framework/tutorial/mf-part-3#section-69828ff0

WPF开发者
「WPF开发者」现役微软MVP,专注 WPF 和 Avalonia 技术分享与传播。
 最新文章