mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-11 20:38:58 +08:00
test(cqrs-benchmarks): 补齐 RequestStartup 的 Mediator 对照
- 新增 RequestStartupBenchmarks 的 Mediator 初始化与冷启动 benchmark 路径 - 更新 request 与 handler 契约以复用单文件内的 Mediator concrete host 对照
This commit is contained in:
parent
337ffbd580
commit
8990749d91
@ -17,11 +17,12 @@ using GFramework.Cqrs.Abstractions.Cqrs;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ILogger = GFramework.Core.Abstractions.Logging.ILogger;
|
||||
using GeneratedMediator = Mediator.Mediator;
|
||||
|
||||
namespace GFramework.Cqrs.Benchmarks.Messaging;
|
||||
|
||||
/// <summary>
|
||||
/// 对比 request 宿主的初始化与首次分发成本,作为后续吸收 `Mediator` comparison benchmark 设计的 startup 基线。
|
||||
/// 对比 request 宿主在 GFramework.CQRS、NuGet `Mediator` 与 MediatR 之间的初始化与首次分发成本。
|
||||
/// </summary>
|
||||
[Config(typeof(Config))]
|
||||
public class RequestStartupBenchmarks
|
||||
@ -31,7 +32,9 @@ public class RequestStartupBenchmarks
|
||||
|
||||
private MicrosoftDiContainer _container = null!;
|
||||
private ServiceProvider _serviceProvider = null!;
|
||||
private ServiceProvider _mediatorServiceProvider = null!;
|
||||
private IMediator _mediatr = null!;
|
||||
private GeneratedMediator _mediator = null!;
|
||||
private ICqrsRuntime _runtime = null!;
|
||||
|
||||
/// <summary>
|
||||
@ -63,6 +66,8 @@ public class RequestStartupBenchmarks
|
||||
|
||||
_serviceProvider = CreateMediatRServiceProvider();
|
||||
_mediatr = _serviceProvider.GetRequiredService<IMediator>();
|
||||
_mediatorServiceProvider = CreateMediatorServiceProvider();
|
||||
_mediator = _mediatorServiceProvider.GetRequiredService<GeneratedMediator>();
|
||||
_container = CreateGFrameworkContainer();
|
||||
_runtime = CreateGFrameworkRuntime(_container);
|
||||
}
|
||||
@ -86,7 +91,7 @@ public class RequestStartupBenchmarks
|
||||
[GlobalCleanup]
|
||||
public void Cleanup()
|
||||
{
|
||||
BenchmarkCleanupHelper.DisposeAll(_container, _serviceProvider);
|
||||
BenchmarkCleanupHelper.DisposeAll(_container, _serviceProvider, _mediatorServiceProvider);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -109,6 +114,16 @@ public class RequestStartupBenchmarks
|
||||
return _runtime;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回已构建宿主中的 `Mediator` concrete mediator,作为 source-generated 对照组的初始化句柄。
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("Initialization")]
|
||||
public GeneratedMediator Initialization_Mediator()
|
||||
{
|
||||
return _mediator;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在新宿主上首次发送 request,作为 MediatR 的 cold-start baseline。
|
||||
/// </summary>
|
||||
@ -133,6 +148,18 @@ public class RequestStartupBenchmarks
|
||||
return await runtime.SendAsync(BenchmarkContext.Instance, Request, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在新的 `Mediator` 宿主上首次发送 request,量化 source-generated concrete path 的 cold-start 成本。
|
||||
/// </summary>
|
||||
[Benchmark]
|
||||
[BenchmarkCategory("ColdStart")]
|
||||
public async ValueTask<BenchmarkResponse> ColdStart_Mediator()
|
||||
{
|
||||
using var serviceProvider = CreateMediatorServiceProvider();
|
||||
var mediator = serviceProvider.GetRequiredService<GeneratedMediator>();
|
||||
return await mediator.Send(Request, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建只承载当前 benchmark request 的最小 GFramework.CQRS runtime。
|
||||
/// </summary>
|
||||
@ -170,6 +197,14 @@ public class RequestStartupBenchmarks
|
||||
ServiceLifetime.Transient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建只承载当前 benchmark request 的最小 `Mediator` 对照宿主。
|
||||
/// </summary>
|
||||
private static ServiceProvider CreateMediatorServiceProvider()
|
||||
{
|
||||
return BenchmarkHostFactory.CreateMediatorServiceProvider(configure: null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为 benchmark 创建稳定的 fatal 级 logger,避免把日志成本混入 startup 测量。
|
||||
/// </summary>
|
||||
@ -188,6 +223,7 @@ public class RequestStartupBenchmarks
|
||||
/// <param name="Id">请求标识。</param>
|
||||
public sealed record BenchmarkRequest(Guid Id) :
|
||||
GFramework.Cqrs.Abstractions.Cqrs.IRequest<BenchmarkResponse>,
|
||||
Mediator.IRequest<BenchmarkResponse>,
|
||||
MediatR.IRequest<BenchmarkResponse>;
|
||||
|
||||
/// <summary>
|
||||
@ -197,10 +233,11 @@ public class RequestStartupBenchmarks
|
||||
public sealed record BenchmarkResponse(Guid Id);
|
||||
|
||||
/// <summary>
|
||||
/// 同时实现 GFramework.CQRS 与 MediatR 契约的最小 request handler。
|
||||
/// 同时实现 GFramework.CQRS、NuGet `Mediator` 与 MediatR 契约的最小 request handler。
|
||||
/// </summary>
|
||||
public sealed class BenchmarkRequestHandler :
|
||||
GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<BenchmarkRequest, BenchmarkResponse>,
|
||||
Mediator.IRequestHandler<BenchmarkRequest, BenchmarkResponse>,
|
||||
MediatR.IRequestHandler<BenchmarkRequest, BenchmarkResponse>
|
||||
{
|
||||
/// <summary>
|
||||
@ -211,6 +248,16 @@ public class RequestStartupBenchmarks
|
||||
return ValueTask.FromResult(new BenchmarkResponse(request.Id));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理 NuGet `Mediator` request。
|
||||
/// </summary>
|
||||
ValueTask<BenchmarkResponse> Mediator.IRequestHandler<BenchmarkRequest, BenchmarkResponse>.Handle(
|
||||
BenchmarkRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
return Handle(request, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理 MediatR request。
|
||||
/// </summary>
|
||||
|
||||
@ -309,6 +309,60 @@ internal sealed class CqrsDispatcherCacheTests
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证同一 request dispatch binding 先走零行为直连路径时不会提前创建 pipeline executor,
|
||||
/// 后续另一 dispatcher 命中相同 binding 且存在行为时,仍会按实际行为数量补建缓存 executor。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Dispatcher_Should_Create_Request_Pipeline_Executor_Only_When_Shared_Binding_Sees_Behaviors()
|
||||
{
|
||||
var requestBindings = GetCacheField("RequestDispatchBindings");
|
||||
var behaviorType = typeof(IPipelineBehavior<DispatcherPipelineCacheRequest, int>);
|
||||
using var zeroBehaviorContainer = CreateFrozenContainer(
|
||||
new DispatcherCacheFixtureOptions
|
||||
{
|
||||
IncludeRequestPipelineCacheBehavior = false
|
||||
});
|
||||
var zeroBehaviorContext = new ArchitectureContext(zeroBehaviorContainer);
|
||||
var behaviorContext = new ArchitectureContext(_container!);
|
||||
var zeroBehaviorDispatcher = GetDispatcherFromContext(zeroBehaviorContext);
|
||||
var behaviorDispatcher = GetDispatcherFromContext(behaviorContext);
|
||||
|
||||
await zeroBehaviorContext.SendRequestAsync(new DispatcherPipelineCacheRequest());
|
||||
|
||||
var bindingAfterZeroBehaviorDispatch = GetPairCacheValue(
|
||||
requestBindings,
|
||||
typeof(DispatcherPipelineCacheRequest),
|
||||
typeof(int));
|
||||
var executorAfterZeroBehaviorDispatch = GetRequestPipelineExecutorValue(
|
||||
requestBindings,
|
||||
typeof(DispatcherPipelineCacheRequest),
|
||||
typeof(int),
|
||||
1);
|
||||
|
||||
await behaviorContext.SendRequestAsync(new DispatcherPipelineCacheRequest());
|
||||
|
||||
var bindingAfterBehaviorDispatch = GetPairCacheValue(
|
||||
requestBindings,
|
||||
typeof(DispatcherPipelineCacheRequest),
|
||||
typeof(int));
|
||||
var executorAfterBehaviorDispatch = GetRequestPipelineExecutorValue(
|
||||
requestBindings,
|
||||
typeof(DispatcherPipelineCacheRequest),
|
||||
typeof(int),
|
||||
1);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(bindingAfterZeroBehaviorDispatch, Is.Not.Null);
|
||||
Assert.That(bindingAfterBehaviorDispatch, Is.SameAs(bindingAfterZeroBehaviorDispatch));
|
||||
Assert.That(executorAfterZeroBehaviorDispatch, Is.Null);
|
||||
Assert.That(executorAfterBehaviorDispatch, Is.Not.Null);
|
||||
AssertRequestBehaviorPresenceEquals(zeroBehaviorDispatcher, behaviorType, false);
|
||||
AssertRequestBehaviorPresenceEquals(behaviorDispatcher, behaviorType, true);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 stream pipeline executor 会按行为数量在 binding 内首次创建并在后续建流中复用。
|
||||
/// </summary>
|
||||
@ -374,6 +428,60 @@ internal sealed class CqrsDispatcherCacheTests
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证同一 stream dispatch binding 先走零行为直连路径时不会提前创建 pipeline executor,
|
||||
/// 后续另一 dispatcher 命中相同 binding 且存在行为时,仍会按实际行为数量补建缓存 executor。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Dispatcher_Should_Create_Stream_Pipeline_Executor_Only_When_Shared_Binding_Sees_Behaviors()
|
||||
{
|
||||
var streamBindings = GetCacheField("StreamDispatchBindings");
|
||||
var behaviorType = typeof(IStreamPipelineBehavior<DispatcherCacheStreamRequest, int>);
|
||||
using var zeroBehaviorContainer = CreateFrozenContainer(
|
||||
new DispatcherCacheFixtureOptions
|
||||
{
|
||||
IncludeStreamPipelineCacheBehavior = false
|
||||
});
|
||||
var zeroBehaviorContext = new ArchitectureContext(zeroBehaviorContainer);
|
||||
var behaviorContext = new ArchitectureContext(_container!);
|
||||
var zeroBehaviorDispatcher = GetDispatcherFromContext(zeroBehaviorContext);
|
||||
var behaviorDispatcher = GetDispatcherFromContext(behaviorContext);
|
||||
|
||||
await DrainAsync(zeroBehaviorContext.CreateStream(new DispatcherCacheStreamRequest()));
|
||||
|
||||
var bindingAfterZeroBehaviorDispatch = GetPairCacheValue(
|
||||
streamBindings,
|
||||
typeof(DispatcherCacheStreamRequest),
|
||||
typeof(int));
|
||||
var executorAfterZeroBehaviorDispatch = GetStreamPipelineExecutorValue(
|
||||
streamBindings,
|
||||
typeof(DispatcherCacheStreamRequest),
|
||||
typeof(int),
|
||||
1);
|
||||
|
||||
await DrainAsync(behaviorContext.CreateStream(new DispatcherCacheStreamRequest()));
|
||||
|
||||
var bindingAfterBehaviorDispatch = GetPairCacheValue(
|
||||
streamBindings,
|
||||
typeof(DispatcherCacheStreamRequest),
|
||||
typeof(int));
|
||||
var executorAfterBehaviorDispatch = GetStreamPipelineExecutorValue(
|
||||
streamBindings,
|
||||
typeof(DispatcherCacheStreamRequest),
|
||||
typeof(int),
|
||||
1);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(bindingAfterZeroBehaviorDispatch, Is.Not.Null);
|
||||
Assert.That(bindingAfterBehaviorDispatch, Is.SameAs(bindingAfterZeroBehaviorDispatch));
|
||||
Assert.That(executorAfterZeroBehaviorDispatch, Is.Null);
|
||||
Assert.That(executorAfterBehaviorDispatch, Is.Not.Null);
|
||||
AssertStreamBehaviorPresenceEquals(zeroBehaviorDispatcher, behaviorType, false);
|
||||
AssertStreamBehaviorPresenceEquals(behaviorDispatcher, behaviorType, true);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证复用缓存的 request pipeline executor 后,行为顺序和最终处理器顺序保持不变。
|
||||
/// </summary>
|
||||
@ -492,6 +600,71 @@ internal sealed class CqrsDispatcherCacheTests
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证同一 request dispatch binding 先在零行为 dispatcher 上命中后,
|
||||
/// 后续切换到存在行为的 dispatcher 仍会重新解析 behavior/handler,并为当前上下文重新注入架构实例。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Dispatcher_Should_Reinject_Current_Request_Context_When_Shared_Binding_Switches_From_Zero_Pipeline()
|
||||
{
|
||||
DispatcherPipelineContextRefreshState.Reset();
|
||||
|
||||
var requestBindings = GetCacheField("RequestDispatchBindings");
|
||||
using var zeroBehaviorContainer = CreateFrozenContainer(
|
||||
new DispatcherCacheFixtureOptions
|
||||
{
|
||||
IncludeRequestPipelineContextRefreshBehavior = false
|
||||
});
|
||||
var zeroBehaviorContext = new ArchitectureContext(zeroBehaviorContainer);
|
||||
var behaviorContext = new ArchitectureContext(_container!);
|
||||
|
||||
await zeroBehaviorContext.SendRequestAsync(new DispatcherPipelineContextRefreshRequest("without-behavior"));
|
||||
|
||||
var bindingAfterZeroBehaviorDispatch = GetPairCacheValue(
|
||||
requestBindings,
|
||||
typeof(DispatcherPipelineContextRefreshRequest),
|
||||
typeof(int));
|
||||
var executorAfterZeroBehaviorDispatch = GetRequestPipelineExecutorValue(
|
||||
requestBindings,
|
||||
typeof(DispatcherPipelineContextRefreshRequest),
|
||||
typeof(int),
|
||||
1);
|
||||
|
||||
await behaviorContext.SendRequestAsync(new DispatcherPipelineContextRefreshRequest("with-behavior"));
|
||||
|
||||
var bindingAfterBehaviorDispatch = GetPairCacheValue(
|
||||
requestBindings,
|
||||
typeof(DispatcherPipelineContextRefreshRequest),
|
||||
typeof(int));
|
||||
var executorAfterBehaviorDispatch = GetRequestPipelineExecutorValue(
|
||||
requestBindings,
|
||||
typeof(DispatcherPipelineContextRefreshRequest),
|
||||
typeof(int),
|
||||
1);
|
||||
var behaviorSnapshots = DispatcherPipelineContextRefreshState.BehaviorSnapshots.ToArray();
|
||||
var handlerSnapshots = DispatcherPipelineContextRefreshState.HandlerSnapshots.ToArray();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(bindingAfterZeroBehaviorDispatch, Is.Not.Null);
|
||||
Assert.That(bindingAfterBehaviorDispatch, Is.SameAs(bindingAfterZeroBehaviorDispatch));
|
||||
Assert.That(executorAfterZeroBehaviorDispatch, Is.Null);
|
||||
Assert.That(executorAfterBehaviorDispatch, Is.Not.Null);
|
||||
|
||||
Assert.That(behaviorSnapshots, Has.Length.EqualTo(1));
|
||||
Assert.That(behaviorSnapshots[0].DispatchId, Is.EqualTo("with-behavior"));
|
||||
Assert.That(behaviorSnapshots[0].Context, Is.SameAs(behaviorContext));
|
||||
|
||||
Assert.That(handlerSnapshots, Has.Length.EqualTo(2));
|
||||
Assert.That(handlerSnapshots[0].DispatchId, Is.EqualTo("without-behavior"));
|
||||
Assert.That(handlerSnapshots[0].Context, Is.SameAs(zeroBehaviorContext));
|
||||
Assert.That(handlerSnapshots[1].DispatchId, Is.EqualTo("with-behavior"));
|
||||
Assert.That(handlerSnapshots[1].Context, Is.SameAs(behaviorContext));
|
||||
Assert.That(handlerSnapshots[1].Context, Is.Not.SameAs(handlerSnapshots[0].Context));
|
||||
Assert.That(handlerSnapshots[1].InstanceId, Is.Not.EqualTo(handlerSnapshots[0].InstanceId));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证缓存的 notification dispatch binding 在重复分发时仍会重新解析 handler,
|
||||
/// 并为当次实例重新注入当前架构上下文。
|
||||
@ -632,6 +805,108 @@ internal sealed class CqrsDispatcherCacheTests
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证同一 stream dispatch binding 先在零行为 dispatcher 上命中后,
|
||||
/// 后续切换到存在行为的 dispatcher 仍会重新解析 behavior/handler,并为当前上下文重新注入架构实例。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task Dispatcher_Should_Reinject_Current_Stream_Context_When_Shared_Binding_Switches_From_Zero_Pipeline()
|
||||
{
|
||||
DispatcherStreamContextRefreshState.Reset();
|
||||
|
||||
var streamBindings = GetCacheField("StreamDispatchBindings");
|
||||
using var zeroBehaviorContainer = CreateFrozenContainer(
|
||||
new DispatcherCacheFixtureOptions
|
||||
{
|
||||
IncludeStreamPipelineContextRefreshBehavior = false
|
||||
});
|
||||
var zeroBehaviorContext = new ArchitectureContext(zeroBehaviorContainer);
|
||||
var behaviorContext = new ArchitectureContext(_container!);
|
||||
|
||||
await DrainAsync(zeroBehaviorContext.CreateStream(new DispatcherStreamContextRefreshRequest("without-behavior")));
|
||||
|
||||
var bindingAfterZeroBehaviorDispatch = GetPairCacheValue(
|
||||
streamBindings,
|
||||
typeof(DispatcherStreamContextRefreshRequest),
|
||||
typeof(int));
|
||||
var executorAfterZeroBehaviorDispatch = GetStreamPipelineExecutorValue(
|
||||
streamBindings,
|
||||
typeof(DispatcherStreamContextRefreshRequest),
|
||||
typeof(int),
|
||||
1);
|
||||
|
||||
await DrainAsync(behaviorContext.CreateStream(new DispatcherStreamContextRefreshRequest("with-behavior")));
|
||||
|
||||
var bindingAfterBehaviorDispatch = GetPairCacheValue(
|
||||
streamBindings,
|
||||
typeof(DispatcherStreamContextRefreshRequest),
|
||||
typeof(int));
|
||||
var executorAfterBehaviorDispatch = GetStreamPipelineExecutorValue(
|
||||
streamBindings,
|
||||
typeof(DispatcherStreamContextRefreshRequest),
|
||||
typeof(int),
|
||||
1);
|
||||
var behaviorSnapshots = DispatcherStreamContextRefreshState.BehaviorSnapshots.ToArray();
|
||||
var handlerSnapshots = DispatcherStreamContextRefreshState.HandlerSnapshots.ToArray();
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(bindingAfterZeroBehaviorDispatch, Is.Not.Null);
|
||||
Assert.That(bindingAfterBehaviorDispatch, Is.SameAs(bindingAfterZeroBehaviorDispatch));
|
||||
Assert.That(executorAfterZeroBehaviorDispatch, Is.Null);
|
||||
Assert.That(executorAfterBehaviorDispatch, Is.Not.Null);
|
||||
|
||||
Assert.That(behaviorSnapshots, Has.Length.EqualTo(1));
|
||||
Assert.That(behaviorSnapshots[0].DispatchId, Is.EqualTo("with-behavior"));
|
||||
Assert.That(behaviorSnapshots[0].Context, Is.SameAs(behaviorContext));
|
||||
|
||||
Assert.That(handlerSnapshots, Has.Length.EqualTo(2));
|
||||
Assert.That(handlerSnapshots[0].DispatchId, Is.EqualTo("without-behavior"));
|
||||
Assert.That(handlerSnapshots[0].Context, Is.SameAs(zeroBehaviorContext));
|
||||
Assert.That(handlerSnapshots[1].DispatchId, Is.EqualTo("with-behavior"));
|
||||
Assert.That(handlerSnapshots[1].Context, Is.SameAs(behaviorContext));
|
||||
Assert.That(handlerSnapshots[1].Context, Is.Not.SameAs(handlerSnapshots[0].Context));
|
||||
Assert.That(handlerSnapshots[1].InstanceId, Is.Not.EqualTo(handlerSnapshots[0].InstanceId));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 描述缓存测试 fixture 需要启用的可选 pipeline 行为集合,
|
||||
/// 用于构造“同一静态 binding 对应不同 dispatcher 注册可见性”的组合场景。
|
||||
/// </summary>
|
||||
private sealed class DispatcherCacheFixtureOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取是否注册 <see cref="DispatcherPipelineCacheBehavior" />。
|
||||
/// </summary>
|
||||
public bool IncludeRequestPipelineCacheBehavior { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否注册 <see cref="DispatcherPipelineContextRefreshBehavior" />。
|
||||
/// </summary>
|
||||
public bool IncludeRequestPipelineContextRefreshBehavior { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否注册 request 顺序验证所需的两层 pipeline 行为。
|
||||
/// </summary>
|
||||
public bool IncludeRequestPipelineOrderBehaviors { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否注册 <see cref="DispatcherStreamPipelineCacheBehavior" />。
|
||||
/// </summary>
|
||||
public bool IncludeStreamPipelineCacheBehavior { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否注册 <see cref="DispatcherStreamPipelineContextRefreshBehavior" />。
|
||||
/// </summary>
|
||||
public bool IncludeStreamPipelineContextRefreshBehavior { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否注册 stream 顺序验证所需的两层 pipeline 行为。
|
||||
/// </summary>
|
||||
public bool IncludeStreamPipelineOrderBehaviors { get; init; } = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过反射读取 dispatcher 的静态缓存对象。
|
||||
/// </summary>
|
||||
@ -679,10 +954,11 @@ internal sealed class CqrsDispatcherCacheTests
|
||||
/// 创建与当前 fixture 注册形状一致、但拥有独立 runtime 实例的冻结容器,
|
||||
/// 用于验证 dispatcher 的实例级缓存不会跨容器共享。
|
||||
/// </summary>
|
||||
private static MicrosoftDiContainer CreateFrozenContainer()
|
||||
/// <param name="options">控制当前隔离容器要启用哪些可选 pipeline 行为的配置。</param>
|
||||
private static MicrosoftDiContainer CreateFrozenContainer(DispatcherCacheFixtureOptions? options = null)
|
||||
{
|
||||
var container = new MicrosoftDiContainer();
|
||||
ConfigureDispatcherCacheFixture(container);
|
||||
ConfigureDispatcherCacheFixture(container, options);
|
||||
|
||||
container.Freeze();
|
||||
return container;
|
||||
@ -692,16 +968,44 @@ internal sealed class CqrsDispatcherCacheTests
|
||||
/// 组装当前 fixture 依赖的 CQRS 容器注册形状,确保默认上下文与隔离容器复用同一份装配基线。
|
||||
/// </summary>
|
||||
/// <param name="container">待补齐 CQRS 注册的目标容器。</param>
|
||||
private static void ConfigureDispatcherCacheFixture(MicrosoftDiContainer container)
|
||||
/// <param name="options">控制是否跳过特定 pipeline 行为注册的可选配置。</param>
|
||||
private static void ConfigureDispatcherCacheFixture(
|
||||
MicrosoftDiContainer container,
|
||||
DispatcherCacheFixtureOptions? options = null)
|
||||
{
|
||||
container.RegisterCqrsPipelineBehavior<DispatcherPipelineCacheBehavior>();
|
||||
container.RegisterCqrsPipelineBehavior<DispatcherPipelineContextRefreshBehavior>();
|
||||
container.RegisterCqrsPipelineBehavior<DispatcherPipelineOrderOuterBehavior>();
|
||||
container.RegisterCqrsPipelineBehavior<DispatcherPipelineOrderInnerBehavior>();
|
||||
container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineCacheBehavior>();
|
||||
container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineContextRefreshBehavior>();
|
||||
container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineOrderOuterBehavior>();
|
||||
container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineOrderInnerBehavior>();
|
||||
options ??= new DispatcherCacheFixtureOptions();
|
||||
|
||||
if (options.IncludeRequestPipelineCacheBehavior)
|
||||
{
|
||||
container.RegisterCqrsPipelineBehavior<DispatcherPipelineCacheBehavior>();
|
||||
}
|
||||
|
||||
if (options.IncludeRequestPipelineContextRefreshBehavior)
|
||||
{
|
||||
container.RegisterCqrsPipelineBehavior<DispatcherPipelineContextRefreshBehavior>();
|
||||
}
|
||||
|
||||
if (options.IncludeRequestPipelineOrderBehaviors)
|
||||
{
|
||||
container.RegisterCqrsPipelineBehavior<DispatcherPipelineOrderOuterBehavior>();
|
||||
container.RegisterCqrsPipelineBehavior<DispatcherPipelineOrderInnerBehavior>();
|
||||
}
|
||||
|
||||
if (options.IncludeStreamPipelineCacheBehavior)
|
||||
{
|
||||
container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineCacheBehavior>();
|
||||
}
|
||||
|
||||
if (options.IncludeStreamPipelineContextRefreshBehavior)
|
||||
{
|
||||
container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineContextRefreshBehavior>();
|
||||
}
|
||||
|
||||
if (options.IncludeStreamPipelineOrderBehaviors)
|
||||
{
|
||||
container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineOrderOuterBehavior>();
|
||||
container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineOrderInnerBehavior>();
|
||||
}
|
||||
|
||||
CqrsTestRuntime.RegisterHandlers(
|
||||
container,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user