增加Mediat Pip特性,并用实例来做一个demo

This commit is contained in:
chfei committed 2026-10-22 17:32:58 +08:00
1 parent 292cb8638c
commit c9ac7412ab
19 files changed
+682 -108

No files matched your search

@@ -0,0 +1,71 @@
using MAX.Framework.Mediation;
// Adapted from ITIXO/MediatR.Extensions.AttributedBehaviors (MIT).
// Copyright (c) 2021 ITIXO s.r.o. See ThirdPartyNotices/MediatR.Extensions.AttributedBehaviors.LICENSE.txt.
using System.Reflection;
using MAX.Core.Mediation;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace MAX.Framework.DependencyInjection;
public static class MediatRBehaviorServiceCollectionExtensions
{
/// <summary>扫描命令特性,按声明行号注册闭合管道,适配 MediatR 9。</summary>
public static IServiceCollection AddMediatRAttributedBehaviors(this IServiceCollection services,
params Assembly[] assemblies)
{
ArgumentNullException.ThrowIfNull(services);
ArgumentNullException.ThrowIfNull(assemblies);
// 先完成全部校验,避免无效配置留下部分注册。
var registrations = new List<ServiceDescriptor>();
foreach (var request in assemblies.Distinct().SelectMany(a => a.DefinedTypes))
{
var attributes = request.GetCustomAttributes<MediatRBehaviorAttribute>(false)
.OrderBy(a => a.LineNumber).ToArray();
if (attributes.Length == 0) continue;
if (request.IsAbstract || request.ContainsGenericParameters)
throw new InvalidOperationException($"{request.FullName} 必须是具体的闭合请求类型。");
var responses = request.GetInterfaces()
.Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IRequest<>))
.Select(i => i.GetGenericArguments()[0]).Distinct().ToArray();
if (responses.Length != 1)
throw new InvalidOperationException($"{request.FullName} 必须实现唯一的 IRequest<TResponse>(无返回值使用 IRequest)。");
if (attributes.GroupBy(a => a.LineNumber).Any(g => g.Count() > 1))
throw new InvalidOperationException($"{request.FullName} 的管道特性必须独占一行,不能使用相同的行号。");
if (attributes.GroupBy(a => a.BehaviorType).Any(g => g.Count() > 1))
throw new InvalidOperationException($"{request.FullName} 重复声明了同一个管道。");
var serviceType = typeof(IPipelineBehavior<,>).MakeGenericType(request.AsType(), responses[0]);
foreach (var attribute in attributes)
{
var behavior = attribute.BehaviorType;
if (behavior.IsGenericTypeDefinition)
{
if (behavior.GetGenericArguments().Length != 2)
throw new InvalidOperationException($"开放泛型管道 {behavior} 必须具有 TRequest、TResponse 两个泛型参数。");
try
{
behavior = behavior.MakeGenericType(request.AsType(), responses[0]);
}
catch (ArgumentException exception)
{
throw new InvalidOperationException($"管道 {behavior} 的泛型约束与请求 {request.FullName} 不匹配。", exception);
}
}
if (!behavior.IsClass || behavior.IsAbstract || behavior.ContainsGenericParameters
|| !serviceType.IsAssignableFrom(behavior))
throw new InvalidOperationException($"管道 {behavior.FullName} 必须是实现 {serviceType} 的具体闭合类型。");
registrations.Add(ServiceDescriptor.Transient(serviceType, behavior));
}
}
services.TryAddSingleton<UnitOfWorkExecutionContext>();
services.TryAddTransient<ServiceFactory>(provider => provider.GetRequiredService);
services.TryAddTransient<IMediator, Mediator>();
services.TryAddTransient<ISender>(provider => provider.GetRequiredService<IMediator>());
services.TryAddTransient<IPublisher>(provider => provider.GetRequiredService<IMediator>());
foreach (var registration in registrations)
services.TryAddEnumerable(registration);
return services;
}
}
@@ -64,6 +64,7 @@ namespace MAX.Framework.DependencyInjection
RunDebugStep("Plugin", "注册全部插件", () => RunPlugins(host, assemblies, instances));
RunDebugStep("Startup", "初始化并注册启动服务", () => RunStartups(host.Services, assemblies, instances));
RunDebugStep("MediatR", "按命令特性注册管道", () => host.Services.AddMediatRAttributedBehaviors(assemblies.ToArray()));