LOGO OA教程 ERP教程 模切知識(shí)交流 PMS教程 CRM教程 開(kāi)發(fā)文檔 其他文檔  
 
網(wǎng)站管理員

在Winform系統(tǒng)開(kāi)發(fā)中,使用MediatR來(lái)實(shí)現(xiàn)類似事件總線的消息處理

freeflydom
2024年1月25日 8:58 本文熱度 1087

MediatR是一款進(jìn)程內(nèi)的消息訂閱、發(fā)布框架,可實(shí)現(xiàn)請(qǐng)求/響應(yīng)、命令、查詢、通知和事件的消息傳遞,解耦了消息處理器和消息之間耦合。提供了Send方法用于發(fā)布到單個(gè)處理程序、Publish方法發(fā)布到多個(gè)處理程序,使用起來(lái)非常方便。目前支持 .NET Framework 、.NET Stardand、.NETCore等版本,可跨平臺(tái)使用。本篇隨筆介紹在Winform系統(tǒng)開(kāi)發(fā)中,使用MediatR來(lái)實(shí)現(xiàn)類似事件總線的消息處理。

1、安裝使用MediatR

MediatR的GitHub項(xiàng)目地址:https://github.com/jbogard/MediatR

MediatR的各種場(chǎng)景使用代碼:https://github.com/jbogard/MediatR/wiki

如果我們?cè)赩S開(kāi)發(fā)項(xiàng)目,我們?cè)贜ugget上找到對(duì)應(yīng)模塊,直接添加到項(xiàng)目引用即可,如下所示。

MediatR使用 Microsoft.Extensions.DependencyInjection.Abstractions 來(lái) 注入服務(wù)處理,我們使用MediatR的時(shí)候,首先需要構(gòu)造ServiceCollection,然后添加配置到其中。


// IServiceCollection負(fù)責(zé)注冊(cè)
IServiceCollection services = new ServiceCollection();
//注冊(cè)MediatR服務(wù),用于測(cè)試MediatR的服務(wù)
services.AddMediatR(cfg => {
    cfg.RegisterServicesFromAssembly(typeof(Portal).Assembly);
});

使用注入服務(wù)的時(shí)候,我們需要獲得其中的ServiceProvider,如下通過(guò)BuildServiceProvider 獲得該對(duì)象。

 IServiceProvider provider = services.BuildServiceProvider();

然后我們創(chuàng)建一個(gè)靜態(tài)類來(lái)存儲(chǔ)這個(gè)對(duì)象。

//存儲(chǔ)全局IServiceProvider的接口實(shí)例, 便于后續(xù)獲得接口實(shí)例ServiceLocator.ConfigService(provider);

其中靜態(tài)類 ServiceLocator 的代碼如下所示。


/// <summary>
    /// 全局存儲(chǔ)IServiceProvider
    /// </summary>
    public class ServiceLocator
    {
        /// <summary>
        /// IOC中的IServiceProvider對(duì)象接口
        /// </summary>
        public static IServiceProvider SerivcePovider { get; private set; }
        /// <summary>
        /// 賦值IServiceProvider到靜態(tài)變量中
        /// </summary>
        /// <param name="provider">IServiceProvider對(duì)象接口</param>
        public static void ConfigService(IServiceProvider provider)
        {
            SerivcePovider = provider;
        }
        /// <summary>
        /// 獲取指定服務(wù)接口實(shí)例
        /// </summary>
        /// <returns></returns>
        public static T GetService<T>()
        {
            return SerivcePovider.GetService<T>();
        }
    }

后面我們就可以通過(guò)該靜態(tài)類的 GetService<T>() 方法獲取對(duì)應(yīng)的注入接口IMediator,我們需要利用該接口來(lái)發(fā)送Send請(qǐng)求/應(yīng)答命令或者發(fā)布Publish消息的處理。例如我們?cè)诖绑w對(duì)象中定義該接口,用于實(shí)際的相關(guān)命令、消息的處理。


public partial class TestMediatR : BaseForm
    {
        private readonly IMediator _mediator;
        public TestMediatR()
        {
            InitializeComponent();
            _mediator = ServiceLocator.GetService<IMediator>();
        }

 

2、MediatR命令或者消息的處理

MediatR是一個(gè)跨平臺(tái)通過(guò)一種進(jìn)程內(nèi)消息傳遞機(jī)制,進(jìn)行請(qǐng)求/響應(yīng)、命令、查詢、通知和事件的消息傳遞,并通過(guò)C#泛型來(lái)支持消息的智能調(diào)度,其目的是消息發(fā)送和消息處理的解耦。它支持以單播和多播形式使用同步或異步的模式來(lái)發(fā)布消息,創(chuàng)建和偵聽(tīng)事件。它主要的幾個(gè)對(duì)象:

  IMediator:主要提供Send與Publish方法,需要執(zhí)行的命令都是通過(guò)這兩個(gè)方法實(shí)現(xiàn)

  IRequest、IRequest<T>命令查詢 | 處理類所繼承的接口,一個(gè)有返回類型,一個(gè)無(wú)返回類型,一個(gè)查詢對(duì)應(yīng)一個(gè)處理類,程序集只認(rèn)第一個(gè)掃描到的類。

  IRequestHandler<in TRequest,TResponse>(實(shí)現(xiàn)Handle方法) :命令處理接口。命令查詢 | 處理類繼承它,也可以繼承AsyncRequestHandler(實(shí)現(xiàn)抽象Handle方法)、RequestHandler(實(shí)現(xiàn)抽象Handle方法)接口

  INotification命令查詢 | 處理類所繼承的接口這個(gè)沒(méi)有返回,與IRequest不通的是可以對(duì)于多個(gè)處理類。

  INotificationHandler<in TNotification>:與IRequestHandler一樣的只不過(guò)這是INotification的處理接口。

Request/Response模式對(duì)象定義


/// <summary>
    /// 請(qǐng)求類
    /// </summary>
    public class RetrieveInfoCommandRequest : IRequest<RetrieveInfoCommandResponse>
    {
        public string Text { get; set; }
    }
    /// <summary>
    /// 回應(yīng)消息
    /// </summary>
    public class RetrieveInfoCommandResponse
    {
        public string OutputMessage { get; set; }
    }
    /// <summary>
    /// 請(qǐng)求應(yīng)答處理類
    /// </summary>
    public class RetrieveInfoCommandHandler : IRequestHandler<RetrieveInfoCommandRequest, RetrieveInfoCommandResponse>
    {
        public async Task<RetrieveInfoCommandResponse> Handle(RetrieveInfoCommandRequest request, CancellationToken cancellationToken)
        {
            var response = new RetrieveInfoCommandResponse();
            response.OutputMessage = $"This is an example of MediatR using {request.Text}";
            return response;
        }
    }

例如我們根據(jù)這個(gè)請(qǐng)求、應(yīng)答的消息協(xié)議,以及定義的處理Handler類(唯一一個(gè)),我們可以設(shè)計(jì)一個(gè)Winform界面來(lái)測(cè)試消息的處理。

 界面的代碼如下所示。


/// <summary>
/// 測(cè)試MediatR的窗體例子
/// </summary>
public partial class TestMediatR : BaseForm
{
    private readonly IMediator _mediator;
    public TestMediatR()
    {
        InitializeComponent();
        _mediator = ServiceLocator.GetService<IMediator>();
    }
    /// <summary>
    /// 使用請(qǐng)求、應(yīng)答的消息進(jìn)行測(cè)試,獲得返回結(jié)果后輸出顯示
    /// </summary>
    private async void btnSend_Click(object sender, EventArgs e)
    {
        //應(yīng)答處理
        var outputMessage = await _mediator.Send(new RetrieveInfoCommandRequest
        {
            Text = this.txtSend.Text
        });
        Console.WriteLine(outputMessage.OutputMessage);
        this.txtReceived.AppendText(outputMessage.OutputMessage + Environment.NewLine);
    }

上面的命令消息方式,有返回值,如果不需要返回值,也可以采用這種一一應(yīng)答的方式,那么定義的時(shí)候,繼承IRequest接口即可。


public class OneWay : IRequest { }
    public class OneWayHandler : IRequestHandler<OneWay>
    {
        public Task Handle(OneWay request, CancellationToken cancellationToken)
        {
            // do work
            return Task.CompletedTask;
        }
    }

 

Notification 消息通知模式

如果我們需要類似事件多播的處理,也就是常規(guī)的消息通知處理,我們采用INotification方式。

Notification模式將消息發(fā)布給多個(gè)處理程序,消息的處理沒(méi)有返回值。


/// <summary>
/// 通知類
/// </summary>
public class MyNotification : INotification
{
    public string Message { get; }
    public MyNotification(string message)
    {
        Message = message;
    }
}
/// <summary>
/// Notification處理程序-模塊1
/// </summary>
public class MyNotifyHandler : INotificationHandler<MyNotification>
{
    public Task Handle(MyNotification notification, CancellationToken cancellationToken)
    {
        var message = "模塊1-收到消息:" + notification.Message;
        //MessageDxUtil.ShowTips(message);
        //提示消息
        var alert = new AlertControl();
        alert.FormLocation =  AlertFormLocation.TopRight;
        alert.AutoFormDelay = 3000;
        alert.Show(Portal.gc.MainDialog, message, message);
        // 處理通知
        Console.WriteLine($"Notification處理程序-模塊1-收到消息: {notification.Message}");
        return Task.CompletedTask;
    }
}
/// <summary>
/// Notification處理程序-模塊2
/// </summary>
public class MySecondNotifyHandler : INotificationHandler<MyNotification>
{
    public Task Handle(MyNotification notification, CancellationToken cancellationToken)
    {
        var message = "模塊2-收到消息:" + notification.Message;
        //MessageDxUtil.ShowTips(message);
        //提示消息
        var alert = new AlertControl();
        alert.FormLocation = AlertFormLocation.TopRight;
        alert.AutoFormDelay = 3000;
        alert.Show(Portal.gc.MainDialog, message, message);
        // 處理通知
        Console.WriteLine($"Notification處理程序-模塊2-收到消息: {notification.Message}");
        return Task.CompletedTask;
    }
}/// <summary>
/// 通知類
/// </summary>
public class MyNotification : INotification
{
    public string Message { get; }
    public MyNotification(string message)
    {
        Message = message;
    }
}
/// <summary>
/// Notification處理程序-模塊1
/// </summary>
public class MyNotifyHandler : INotificationHandler<MyNotification>
{
    public Task Handle(MyNotification notification, CancellationToken cancellationToken)
    {
        var message = "模塊1-收到消息:" + notification.Message;
        //MessageDxUtil.ShowTips(message);
        //提示消息
        var alert = new AlertControl();
        alert.FormLocation =  AlertFormLocation.TopRight;
        alert.AutoFormDelay = 3000;
        alert.Show(Portal.gc.MainDialog, message, message);
        // 處理通知
        Console.WriteLine($"Notification處理程序-模塊1-收到消息: {notification.Message}");
        return Task.CompletedTask;
    }
}
/// <summary>
/// Notification處理程序-模塊2
/// </summary>
public class MySecondNotifyHandler : INotificationHandler<MyNotification>
{
    public Task Handle(MyNotification notification, CancellationToken cancellationToken)
    {
        var message = "模塊2-收到消息:" + notification.Message;
        //MessageDxUtil.ShowTips(message);
        //提示消息
        var alert = new AlertControl();
        alert.FormLocation = AlertFormLocation.TopRight;
        alert.AutoFormDelay = 3000;
        alert.Show(Portal.gc.MainDialog, message, message);
        // 處理通知
        Console.WriteLine($"Notification處理程序-模塊2-收到消息: {notification.Message}");
        return Task.CompletedTask;
    }
}

我們?cè)诮缑嫔习l(fā)布消息的代碼如下所示。

private async void btnNotify_Click(object sender, EventArgs e)
{    //發(fā)布消息
    await _mediator.Publish(new MyNotification(this.txtSend.Text));
}

可以看到在控制臺(tái)和UI上我們的都有測(cè)試消息的輸出。

默認(rèn)情況下,MediatR的消息發(fā)布是一個(gè)一個(gè)執(zhí)行的,即便是返回Task的情況,也是使用await等待上一個(gè)執(zhí)行完成后才進(jìn)行下一個(gè)的調(diào)用。如果需要使用并行的方法進(jìn)行調(diào)用,可以進(jìn)行定制,具體可參考官方示例:MediatR.Examples.PublishStrategies

對(duì)于MediatR來(lái)說(shuō),無(wú)論是發(fā)送IRequest類型消息,還是發(fā)布INotification類型消息,都是異步的。這里需要特別留意,即使你使用的是同步的消息處理程序,對(duì)于消息發(fā)布來(lái)說(shuō),都是異步的,與你的處理程序是同步或異步無(wú)關(guān)。

詳細(xì)的介紹,可以參考官方的案例介紹:https://github.com/jbogard/MediatR/wiki

 

3、回顧WPF的MVVM的消息處理

對(duì)于WPF,其實(shí)也是類似采用該組件實(shí)現(xiàn)事件、消息的處理的,不過(guò)如果我們采用MVVM的框架設(shè)計(jì)模式,可以采用MVVM(微軟的 CommunityToolkit.Mvvm的組件包)的內(nèi)置的消息處理模式,我在隨筆《使用WPF開(kāi)發(fā)自定義用戶控件,以及實(shí)現(xiàn)相關(guān)自定義事件的處理》有相關(guān)的介紹。

CommunityToolkit.Mvvm  (又名 MVVM 工具包,以前名為 Microsoft.Toolkit.Mvvm) 是一個(gè)現(xiàn)代、快速且模塊化的 MVVM 庫(kù)。官網(wǎng)介紹地址:https://learn.microsoft.com/zh-cn/dotnet/communitytoolkit/mvvm/

利用MVVM推送一條消息,如下代碼所示。

//發(fā)送MVVM消息信息通知方式(一)WeakReferenceMessenger.Default.Send(new ClickEventMessage(eventData));

而其中 ClickEventMessage 是我們根據(jù)要求定義的一個(gè)消息對(duì)象類,如下代碼所示。

完整的Command命令如下所示。


/// <summary>
/// 雙擊觸發(fā)MVVM消息通知
/// </summary>
/// <param name="typeName">處理類型:Number、Animal、WuHan</param>
/// <returns></returns>
[RelayCommand]
private async Task DoubleClick(string typeName)
{
    var clickType = ClickEventType.Number;
    var clickValue = this.Number;
    ..............//處理不同typeName值邏輯//事件數(shù)據(jù)
    var eventData = new ClickEventData(clickType, clickValue);
    //發(fā)送MVVM消息信息通知方式(一)
    WeakReferenceMessenger.Default.Send(new ClickEventMessage(eventData));
}

通過(guò)這樣的消息發(fā)送,就需要有個(gè)地方來(lái)接收這個(gè)信息的,我們?cè)谛枰幚硎录母复翱谥袛r截處理消息即可。


//處理MVVM的消息通知
WeakReferenceMessenger.Default.Register<ClickEventMessage>(this, (r, m) =>
{
    var data = m.Value;
    var list = ControlHelper.FindVisualChildren<LotteryItemControl>(this.listControl);
    foreach (var lottery in list)
    {
        lottery.SetSelected(data);
    }
});

從而實(shí)現(xiàn)了WPF消息的發(fā)送和應(yīng)答處理。

另外,我在隨筆《使用 FastEndpoints 來(lái)垂直切割Web API的控制器方法》介紹的FastEndpoints 處理機(jī)制,也是類似這樣的模式,有興趣可以了解一下FastEndpoints 的處理。

 

作者:伍華聰

轉(zhuǎn)自:https://www.cnblogs.com/wuhuacong/p/17984987


該文章在 2024/1/25 17:22:35 編輯過(guò)
關(guān)鍵字查詢
相關(guān)文章
正在查詢...
點(diǎn)晴ERP是一款針對(duì)中小制造業(yè)的專業(yè)生產(chǎn)管理軟件系統(tǒng),系統(tǒng)成熟度和易用性得到了國(guó)內(nèi)大量中小企業(yè)的青睞。
點(diǎn)晴PMS碼頭管理系統(tǒng)主要針對(duì)港口碼頭集裝箱與散貨日常運(yùn)作、調(diào)度、堆場(chǎng)、車隊(duì)、財(cái)務(wù)費(fèi)用、相關(guān)報(bào)表等業(yè)務(wù)管理,結(jié)合碼頭的業(yè)務(wù)特點(diǎn),圍繞調(diào)度、堆場(chǎng)作業(yè)而開(kāi)發(fā)的。集技術(shù)的先進(jìn)性、管理的有效性于一體,是物流碼頭及其他港口類企業(yè)的高效ERP管理信息系統(tǒng)。
點(diǎn)晴WMS倉(cāng)儲(chǔ)管理系統(tǒng)提供了貨物產(chǎn)品管理,銷售管理,采購(gòu)管理,倉(cāng)儲(chǔ)管理,倉(cāng)庫(kù)管理,保質(zhì)期管理,貨位管理,庫(kù)位管理,生產(chǎn)管理,WMS管理系統(tǒng),標(biāo)簽打印,條形碼,二維碼管理,批號(hào)管理軟件。
點(diǎn)晴免費(fèi)OA是一款軟件和通用服務(wù)都免費(fèi),不限功能、不限時(shí)間、不限用戶的免費(fèi)OA協(xié)同辦公管理系統(tǒng)。
Copyright 2010-2025 ClickSun All Rights Reserved

黄频国产免费高清视频,久久不卡精品中文字幕一区,激情五月天AV电影在线观看,欧美国产韩国日本一区二区
亚洲午夜片在线 | 亚洲人成网站色在线 | 最新亚洲精品视频在线 | 免费国产黄线在线观看视频 | 亚洲人成网线在线播放 | 日本在亚洲A在线观看 |