日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

MediatR 知多少

發布時間:2023/12/4 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 MediatR 知多少 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

引言

首先不用查字典了,詞典查無此詞。猜測是作者筆誤將Mediator寫成MediatR了。廢話少說,轉入正題。

先來簡單了解下這個開源項目MediatR(作者Jimmy Bogard,也是開源項目AutoMapper的創建者,在此表示膜拜):

Simple mediator implementation in .NET. In-process messaging with no dependencies. Supports request/response, commands, queries, notifications and events, synchronous and async with intelligent dispatching via C# generic variance.

.NET中的簡單中介者模式實現,一種進程內消息傳遞機制(無其他外部依賴)。 支持以同步或異步的形式進行請求/響應,命令,查詢,通知和事件的消息傳遞,并通過C#泛型支持消息的智能調度。

如上所述,其核心是一個中介者模式的.NET實現,其目的是消息發送和消息處理的解耦。它支持以單播和多播形式使用同步或異步的模式來發布消息,創建和偵聽事件。

中介者模式

既然是對中介者模式的一種實現,那么我們就有必要簡要介紹下中介者這個設計模式,以便后續展開。

中介者模式:用一個中介對象封裝一系列的對象交互,中介者使各對象不需要顯示地相互作用,從而使耦合松散,而且可以獨立地改變它們之間的交互。

看上面的官方定義可能還是有點繞,那么下面這張圖應該能幫助你對中介者模式有個直觀了解。

使用中介模式,對象之間的交互將封裝在中介對象中。對象不再直接相互交互(解耦),而是通過中介進行交互。這減少了對象之間的依賴性,從而減少了耦合。

那其優缺點也在圖中很容易看出:

優點:中介者模式的優點就是減少類間的依賴,把原有的一對多的依賴變成了一對一的依賴,同事類只依賴中介者,減少了依賴,當然同時也降低了類間的耦合

缺點:中介者模式的缺點就是中介者會膨脹得很大,而且邏輯復雜,原本N個對象直接的相互依賴關系轉換為中介者和同事類的依賴關系,同事類越多,中介者的邏輯就越復雜。

Hello MeidatR

在開始之前,我們先來了解下其基本用法。

單播消息傳輸

單播消息傳輸,也就是一對一的消息傳遞,一個消息對應一個消息處理。其通過 IRequest來抽象單播消息,用 IRequestHandler進行消息處理。

  • //構建 消息請求

  • public class Ping : IRequest<string> { }

  • //構建 消息處理

  • public class PingHandler : IRequestHandler<Ping, string> {

  • ? ?public Task<string> Handle(Ping request, CancellationToken cancellationToken) {

  • ? ? ? ?return Task.FromResult("Pong");

  • ? ?}

  • }

  • //發送 請求

  • var response = await mediator.Send(new Ping());

  • Debug.WriteLine(response); // "Pong"

  • 多播消息傳輸

    多播消息傳輸,也就是一對多的消息傳遞,一個消息對應多個消息處理。其通過 INotification來抽象多播消息,對應的消息處理類型為 INotificationHandler

  • //構建 通知消息

  • public class Ping : INotification { }

  • //構建 消息處理器1

  • public class Pong1 : INotificationHandler<Ping> {

  • ? ?public Task Handle(Ping notification, CancellationToken cancellationToken) {

  • ? ? ? ?Debug.WriteLine("Pong 1");

  • ? ? ? ?return Task.CompletedTask;

  • ? ?}

  • }

  • //構建 消息處理器2

  • public class Pong2 : INotificationHandler<Ping> {

  • ? ?public Task Handle(Ping notification, CancellationToken cancellationToken) {

  • ? ? ? ?Debug.WriteLine("Pong 2");

  • ? ? ? ?return Task.CompletedTask;

  • ? ?}

  • }


  • //發布消息

  • await mediator.Publish(new Ping());

  • 源碼解析

    對MediatR有了基本認識后,我們來看看源碼,研究下其如何實現的。

    從代碼圖中我們可以看到其核心的對象主要包括:

  • IRequest Vs IRequestHandler

  • INotification Vs INoticifaitonHandler

  • IMediator Vs Mediator

  • Unit

  • IPipelineBehavior

  • IRequest Vs IRequestHandler

    其中 IRequestINotification分別對應單播和多播消息的抽象。 對于單播消息可以決定是否需要返回值選用不同的接口:

    • IRequest?- 有返回值

    • IRequest - 無返回值

    這里就不得不提到其中巧妙的設計,通過引入結構類型 Unit來代表無返回的情況。

  • /// <summary>

  • /// 代表無需返回值的請求

  • /// </summary>

  • public interface IRequest : IRequest<Unit> { }


  • /// <summary>

  • /// 代表有返回值的請求

  • /// </summary>

  • /// <typeparam name="TResponse">Response type</typeparam>

  • public interface IRequest<out TResponse> : IBaseRequest { }


  • /// <summary>

  • /// Allows for generic type constraints of objects implementing IRequest or IRequest{TResponse}

  • /// </summary>

  • public interface IBaseRequest { }

  • 同樣對于 IRequestHandler也是通過結構類型 Unit來處理不需要返回值的情況。

  • public interface IRequestHandler<in TRequest, TResponse>

  • ? ?where TRequest : IRequest<TResponse>

  • {

  • ? ?Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);

  • }


  • public interface IRequestHandler<in TRequest> : IRequestHandler<TRequest, Unit>

  • ? ?where TRequest : IRequest<Unit>

  • {

  • }

  • 從上面我們可以看出定義了一個方法名為 Handle返回值為 Task的包裝類型,而因此賦予了其具有以同步和異步的方式進行消息處理的能力。我們再看一下其以異步方式進行消息處理(無返回值)的默認實現 AsyncRequestHandler

  • public abstract class AsyncRequestHandler<TRequest> : IRequestHandler<TRequest>

  • ? ?where TRequest : IRequest

  • {

  • ? ?async Task<Unit> IRequestHandler<TRequest, Unit>.Handle(TRequest request, CancellationToken cancellationToken)

  • ? ?{

  • ? ? ? ?await Handle(request, cancellationToken).ConfigureAwait(false);

  • ? ? ? ?return Unit.Value;

  • ? ?}


  • ? ?protected abstract Task Handle(TRequest request, CancellationToken cancellationToken);

  • }

  • 從上面的代碼來看,我們很容易看出這是裝飾模式的實現方式,是不是很巧妙的解決了無需返回值的場景。

    最后我們來看下結構類型 Unit的定義:

  • public struct Unit : IEquatable<Unit>, IComparable<Unit>, IComparable

  • {

  • ? ?public static readonly Unit Value = new Unit();


  • ? ?public static readonly Task<Unit> Task = System.Threading.Tasks.Task.FromResult(Value);

  • ? ?// some other code

  • }

  • IMediator Vs Mediator

    IMediator主要定義了兩個方法 SendPublish,分別用于發送消息和發布通知。其默認實現Mediator中定義了兩個集合,分別用來保存請求與請求處理的映射關系。

  • //Mediator.cs

  • //保存request和requesthandler的映射關系,1對1。

  • private static readonly ConcurrentDictionary<Type, object> _requestHandlers = new ConcurrentDictionary<Type, object>();

  • //保存notification與notificationhandler的映射關系,

  • private static readonly ConcurrentDictionary<Type, NotificationHandlerWrapper> _notificationHandlers = new ConcurrentDictionary<Type, NotificationHandlerWrapper>();

  • 這里面其又引入了兩個包裝類: RequestHandlerWrapperNotificationHandlerWrapper。這兩個包裝類的作用就是用來傳遞 ServiceFactory委托進行依賴解析。

    所以說 Mediator借助 publicdelegateobjectServiceFactory(TypeserviceType);完成對Ioc容器的一層抽象。這樣就可以對接任意你喜歡用的Ioc容器,比如:Autofac、Windsor或ASP.NET Core默認的Ioc容器,只需要在注冊 IMediator時指定 ServiceFactory類型的委托即可,比如ASP.NET Core中的做法:

    在使用ASP.NET Core提供的原生Ioc容器有些問題:Service registration crashes when registering generic handlers

    IPipelineBehavior

    MeidatR支持按需配置請求管道進行消息處理。即支持在請求處理前和請求處理后添加額外行為。僅需實現以下兩個接口,并注冊到Ioc容器即可。

    • IRequestPreProcessor?請求處理前接口

    • IRequestPostProcessor?請求處理后接口

    其中 IPipelineBehavior的默認實現: RequestPreProcessorBehaviorRequestPostProcessorBehavior分別用來處理所有實現 IRequestPreProcessorIRequestPostProcessor接口定義的管道行為。

    而處理管道是如何構建的呢?我們來看下 RequestHandlerWrapperImpl的具體實現:

  • internal class RequestHandlerWrapperImpl<TRequest, TResponse> : RequestHandlerWrapper<TResponse>

  • ? ?where TRequest : IRequest<TResponse>

  • {

  • ? ?public override Task<TResponse> Handle(IRequest<TResponse> request, CancellationToken cancellationToken,

  • ? ? ? ?ServiceFactory serviceFactory)

  • ? ?{

  • ? ? ? ?Task<TResponse> Handler() => GetHandler<IRequestHandler<TRequest, TResponse>>(serviceFactory).Handle((TRequest) request, cancellationToken);


  • ? ? ? ?return serviceFactory

  • ? ? ? ? ? ?.GetInstances<IPipelineBehavior<TRequest, TResponse>>()

  • ? ? ? ? ? ?.Reverse()

  • ? ? ? ? ? ?.Aggregate((RequestHandlerDelegate<TResponse>) Handler, (next, pipeline) => () => pipeline.Handle((TRequest)request, cancellationToken, next))();

  • ? ?}

  • }

  • 就這樣一個簡單的函數,涉及的知識點還真不少,說實話我花了不少時間來理清這個邏輯。 那都涉及到哪些知識點呢?我們一個一個的來理一理。

  • C# 7.0的新特性 - 局部函數

  • C# 6.0的新特性 - 表達式形式的成員函數

  • Linq高階函數 -?Aggregate

  • 匿名委托

  • 構造委托函數鏈

  • 關于第1、2個知識點,請看下面這段代碼:

  • public delegate int SumDelegate();//定義委托

  • public static void Main()

  • {

  • ? ?//局部函數(在函數內部定義函數)

  • ? ?//表達式形式的成員函數, 相當于 int Sum() { return 1 + 2;}

  • ? ?int Sum() => 1 + 2;


  • ? ?var sumDelegate = (SumDelegate)Sum;//轉換為委托

  • ? ?Console.WriteLine(sumDelegate());//委托調用,輸出:3

  • }

  • 再看第4個知識點,匿名委托:

  • public delegate int SumDelegate();


  • SumDelegate delegater1 = delegate(){ return 1+2; }

  • //也相當于

  • SumDelegate delegater2 => 1+2;

  • 下面再來介紹一下 Aggregate這個Linq高階函數。 Aggregate是對一個集合序列進行累加操作,通過指定初始值,累加函數,以及結果處理函數完成計算。

    函數定義:

  • public static TResult Aggregate<TSource,TAccumulate,TResult>

  • (this IEnumerable<TSource> source,

  • TAccumulate seed,

  • Func<TAccumulate,TSource,TAccumulate> func,

  • Func<TAccumulate,TResult> resultSelector);

  • 根據函數定義我們來寫個簡單的demo:

  • var nums = Enumerable.Range(2, 3);//[2,3,4]

  • // 計算1到5的累加之和,再將結果乘以2

  • var sum = nums.Aggregate(1, (total, next) => total + next, result => result * 2);// 相當于 (((1+2)+3)+4)*2=20

  • Console.WriteLine(sum);//20

  • 和函數參數進行一一對應:

  • seed : 1

  • Func?func : (total, next) => total + next

  • Func?resultSelector : result => result * 2

  • 基于上面的認識,我們再來回過頭梳理一下 RequestHandlerWrapperImpl。 其主要是借助委托: publicdelegateTask<TResponse>RequestHandlerDelegate<TResponse>();來構造委托函數鏈來構建處理管道。

    Aggregate函數了解后,我們就不難理解處理管道的構建了。請看下圖中的代碼解讀:

    那如何保證先執行 IRequestPreProcessor再執行 IRequestPostProcessor呢? 就是在注冊到Ioc容器時必須保證順序,先注冊 IRequestPreProcessor再注冊 IRequestPostProcessor。(這一點很重要!!!)

    看到這里有沒有想到ASP.NET Core中請求管道中中間件的構建呢?是不是很像俄羅斯套娃?先由內而外構建管道,再由外而內執行!

    至此,MediatR的實現思路算是理清了。

    應用場景

    如文章開頭提到:MediatR是一種進程內消息傳遞機制。 支持以同步或異步的形式進行請求/響應,命令,查詢,通知和事件的消息傳遞,并通過C#泛型支持消息的智能調度。

    那么我們就應該明白,其核心是消息的解耦。因為我們幾乎都是在與消息打交道,那因此它的應用場景就很廣泛,比如我們可以基于MediatR實現CQRS、EventBus等。

    另外,還有一種應用場景:我們知道借助依賴注入的好處是,就是解除依賴,但我們又不得不思考一個問題,隨著業務邏輯復雜度的增加,構造函數可能要注入更多的服務,當注入的依賴太多時,其會導致構造函數膨脹。比如:

  • public DashboardController(

  • ? ?ICustomerRepository customerRepository,

  • ? ?IOrderService orderService,

  • ? ?ICustomerHistoryRepository historyRepository,

  • ? ?IOrderRepository orderRepository,

  • ? ?IProductRespoitory productRespoitory,

  • ? ?IRelatedProductsRepository relatedProductsRepository,

  • ? ?ISupportService supportService,

  • ? ?ILog logger

  • ? ?) ?

  • 如果借助 MediatR進行改造,也許僅需注入 IMediatR就可以了。

  • public DashboardController(IMediatR mediatr) ?

  • 總結

    看到這里,也許你應該明白MediatR實質上并不是嚴格意義上的中介者模式實現,我更傾向于其是基于Ioc容器的一層抽象,根據請求定位相應的請求處理器進行消息處理,也就是服務定位。 那到這里似乎也恍然大悟MediatR這個筆誤可能是有意為之了。序員,你怎么看?

    參考資料:

    CQRS/MediatR implementation patterns

    MediatR when and why I should use it??

    ABP CQRS 實現案例:基于 MediatR 實現

    相關文章:

    • [譯]ASP.NET&nbsp;Core中使用MediatR實現命令和中介者模式

    • 【翻譯】asp.net&nbsp;core中使用MediatR

    • MEDIATR&nbsp;一個低調的中介者類庫

    總結

    以上是生活随笔為你收集整理的MediatR 知多少的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。