重新整理 .net core 實踐篇—————Mediator實踐[二十八]

不問前世發表於2021-06-26

前言

簡單整理一下Mediator。

正文

Mediator 名字是中介者的意思。

那麼它和中介者模式有什麼關係呢?前面整理設計模式的時候,並沒有去介紹具體的中介者模式的程式碼實現。

如下:

https://www.cnblogs.com/aoximin/p/13600464.html

之所以沒寫程式碼就是因為它現在是一種思想,以前的簡單的已經很難滿足我們現在開發的需求了。

那麼看下Mediator 是如何做的吧。

Mediator 這個服務,是如何讓我們的命令查詢職責分離的。

首先安裝一下包:

Mediator 核心介面為:

  1. IMediator

  2. IRequest,IRequest

  3. IRequestHandler<in IRequest,TResponse>

一般從介面就能看到其設計思想,其餘的實現,各有各的想法,那麼就來看下IMediator吧。

/// <summary>
/// Defines a mediator to encapsulate request/response and publishing interaction patterns
/// </summary>
public interface IMediator : ISender, IPublisher
{
}

這個介面的作用是定義了一箇中介者,這個中介者用來裝入請求或者響應和實現一些互動模式。

那麼看來分別就是ISender和IPublisher來實現了。

看下ISender:

/// <summary>
/// Send a request through the mediator pipeline to be handled by a single handler.
/// </summary>
public interface ISender
{
	/// <summary>
	/// Asynchronously send a request to a single handler
	/// </summary>
	/// <typeparam name="TResponse">Response type</typeparam>
	/// <param name="request">Request object</param>
	/// <param name="cancellationToken">Optional cancellation token</param>
	/// <returns>A task that represents the send operation. The task result contains the handler response</returns>
	Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default);

	/// <summary>
	/// Asynchronously send an object request to a single handler via dynamic dispatch
	/// </summary>
	/// <param name="request">Request object</param>
	/// <param name="cancellationToken">Optional cancellation token</param>
	/// <returns>A task that represents the send operation. The task result contains the type erased handler response</returns>
	Task<object?> Send(object request, CancellationToken cancellationToken = default);
}

這個介面用來通過由單個處理程式的中介者管道中傳送請求。

IPublisher:

/// <summary>
/// Publish a notification or event through the mediator pipeline to be handled by multiple handlers.
/// </summary>
public interface IPublisher
{
	/// <summary>
	/// Asynchronously send a notification to multiple handlers
	/// </summary>
	/// <param name="notification">Notification object</param>
	/// <param name="cancellationToken">Optional cancellation token</param>
	/// <returns>A task that represents the publish operation.</returns>
	Task Publish(object notification, CancellationToken cancellationToken = default);

	/// <summary>
	/// Asynchronously send a notification to multiple handlers
	/// </summary>
	/// <param name="notification">Notification object</param>
	/// <param name="cancellationToken">Optional cancellation token</param>
	/// <returns>A task that represents the publish operation.</returns>
	Task Publish<TNotification>(TNotification notification, CancellationToken cancellationToken = default)
		where TNotification : INotification;
}

這個介面用來通過多個處理程式的中介者管道中傳送通知或者事件。

好了,現在我們得到的資訊有"釋出"、"事件"、"請求"、"管道" 這幾個名詞了。

那麼實際上我們也能大致的猜出它是怎麼實現的。

接下來檢視IRequest:

/// <summary>
/// Marker interface to represent a request with a void response
/// </summary>
public interface IRequest : IRequest<Unit> { }

/// <summary>
/// Marker interface to represent a request with a response
/// </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介面:

/// <summary>
/// Defines a handler for a request
/// </summary>
/// <typeparam name="TRequest">The type of request being handled</typeparam>
/// <typeparam name="TResponse">The type of response from the handler</typeparam>
public interface IRequestHandler<in TRequest, TResponse>
	where TRequest : IRequest<TResponse>
{
	/// <summary>
	/// Handles a request
	/// </summary>
	/// <param name="request">The request</param>
	/// <param name="cancellationToken">Cancellation token</param>
	/// <returns>Response from the request</returns>
	Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
}

現在我們在來整理一下名詞:"釋出"、"事件"、"請求"、"管道" 、"處理請求"

那麼大概猜測大概通過介面來處理請求,然後還可以釋出事件處理的。處理請求有處理請求的管道,且這個處理是單個處理程式,而處理事件是多個處理程式。

接下來操作一遍:

async static Task Main(string[] args)
{
	var services = new ServiceCollection();

	services.AddMediatR(typeof(Program).Assembly);

	var serviceProvider = services.BuildServiceProvider();

	var mediator = serviceProvider.GetService<IMediator>();

	await mediator.Send(new SelfCommand{ CommandName ="zhangsan"});
}


internal class SelfCommand : IRequest<long>
{
	public string CommandName { get; set; }
}

internal class SelfCommandHandler : IRequestHandler<SelfCommand, long>
{
	public Task<long> Handle(SelfCommand request, CancellationToken cancellationToken)
	{
		Console.WriteLine($"處理 {nameof(SelfCommand)}請求:{request.CommandName}");
		return Task.FromResult(10L);
	}
}

這裡就有一個疑問了,為啥SelfCommandHandler會自動稱為處理程式?我們並沒有設定啊。

那麼就來看一下send在幹什麼吧:

public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default)
{
	if (request == null)
	{
		throw new ArgumentNullException(nameof(request));
	}

	var requestType = request.GetType();

	var handler = (RequestHandlerWrapper<TResponse>)_requestHandlers.GetOrAdd(requestType,
		t => (RequestHandlerBase)Activator.CreateInstance(typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, typeof(TResponse))));

	return handler.Handle(request, cancellationToken, _serviceFactory);
}

上面可以看到生成了一個handle,然後呼叫了Handle 方法。

然後進RequestHandlerWrapperImpl 檢視:

internal class RequestHandlerWrapperImpl<TRequest, TResponse> : RequestHandlerWrapper<TResponse>
	where TRequest : IRequest<TResponse>
{
	public override Task<object?> Handle(object request, CancellationToken cancellationToken,
		ServiceFactory serviceFactory)
	{
		return Handle((IRequest<TResponse>)request, cancellationToken, serviceFactory)
			.ContinueWith(t =>
			{
				if (t.IsFaulted)
				{
					ExceptionDispatchInfo.Capture(t.Exception.InnerException).Throw();
				}
				return (object?)t.Result;
			}, cancellationToken);
	}

	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))();
	}
}

這裡埋一個坑,因為看了一下,有很多細節的地方比如說Activator.CreateInstance的機制、一些管道細節,還設計到註冊IPipelineBehavior<TRequest, TResponse>的實現類的機制,整理到該系列的細節篇中,將會比較詳細的介紹。

現在只需要看Handle的ContinueWith,如果失敗的話,那麼會返回一個異常,否則返回結果。

然後根據上面的,我們指定Request對應的RequstHandle 必須名字有如下規律:如SelfCommand,只需要SelfCommand加長一些即可,比如說SelfCommandHandler,比如說SelfCommandHandlerV2,還可以SelfCommand2都行。

但是呢,最好改好名字,後面加Handle。

同時,如果我們寫兩個SelfCommandHandler和SelfCommandHandlerV2,那麼是否兩個都會執行?不是的,只會執行一個。

internal class SelfCommandHandler2 : IRequestHandler<SelfCommand, long>
{
	public Task<long> Handle(SelfCommand request, CancellationToken cancellationToken)
	{
		Console.WriteLine($"處理 {nameof(SelfCommand)} V2請求:{request.CommandName}");
		return Task.FromResult(10L);
	}
}

internal class SelfCommandHandler : IRequestHandler<SelfCommand, long>
{
	public Task<long> Handle(SelfCommand request, CancellationToken cancellationToken)
	{
		Console.WriteLine($"處理 {nameof(SelfCommand)}請求:{request.CommandName}");
		return Task.FromResult(10L);
	}
}

比如說這樣,那麼會執行。

也就是說會執行SelfCommandHandler2。

那麼請求就算介紹完了,那麼看下事件。

呼叫事件這樣呼叫即可:

 await mediator.Publish(new SelfEvent { EventName = "SelfEvent" });

具體類:

internal class SelfEvent : INotification
{
	public string EventName { get; set; }
}

internal class SelfEventHandler : INotificationHandler<SelfEvent>
{
	public Task Handle(SelfEvent notification, CancellationToken cancellationToken)
	{
		Console.WriteLine($"SelfEventHandler 執行:{notification.EventName}");

		return Task.CompletedTask;
	}
}

internal class SelfEventHandlerV2 : INotificationHandler<SelfEvent>
{
	public Task Handle(SelfEvent notification, CancellationToken cancellationToken)
	{
		Console.WriteLine($"SelfEventHandlerV2 執行:{notification.EventName}");

		return Task.CompletedTask;
	}
}

效果:

那麼和requst不同的是,註冊幾個就會呼叫幾個。

好了現在回到中介者模式中來。

中介者模式(Mediator Pattern)是用來降低多個物件和類之間的通訊複雜性。這種模式提供了一箇中介類,該類通常處理不同類之間的通訊,並支援鬆耦合,使程式碼易於維護。中介者模式屬於行為型模式。

那麼Mediator 這個模組呢,幫助我們解決了request和requesthandle之間的耦合,和 Event與EventHandle 之間的耦合。

一開始我認為是命令模式,後來一想,命令模式解決“行為請求者”與“行為實現者”的耦合。

命令模式如下:

https://www.cnblogs.com/aoximin/p/13616558.html

上面只是命令模式的一種形式哈。

下一節領域事件的處理。以上只是個人整理,如有錯誤,望請指點。

相關文章