GFramework/GFramework.Tests.Common/CqrsTestRuntime.cs
gewuyou 7209fdc32d docs(cqrs): 收口旧版运行时别名说明
- 更新 LegacyICqrsRuntime 兼容层说明,明确旧命名空间别名与正式 CQRS runtime seam 的边界

- 补充容器基础设施回填 legacy alias 的回归测试,并收敛相关 helper 注释

- 更新 cqrs-rewrite 跟踪与 trace,记录 RP-066 的批处理结果和验证
2026-04-30 11:38:52 +08:00

130 lines
6.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Reflection;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Ioc;
using GFramework.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Command;
using GFramework.Cqrs.Notification;
using LegacyICqrsRuntime = GFramework.Core.Abstractions.Cqrs.ICqrsRuntime;
namespace GFramework.Tests.Common;
/// <summary>
/// 为测试项目提供对 CQRS 处理器真实注册入口的受控访问。
/// </summary>
/// <remarks>
/// 该测试基础设施位于独立模块中,避免多个测试项目复制同一份反射绑定与默认 runtime 接线逻辑。
/// 测试应通过该入口驱动注册流程,而不是各自维护一份实现细节副本。
/// </remarks>
public static class CqrsTestRuntime
{
private static readonly Assembly CqrsRuntimeAssembly = typeof(CommandBase<,>).Assembly;
private static readonly Type CqrsHandlerRegistrarType = CqrsRuntimeAssembly
.GetType(
"GFramework.Cqrs.Internal.CqrsHandlerRegistrar",
throwOnError: true)!;
private static readonly MethodInfo RegisterHandlersMethod = CqrsHandlerRegistrarType
.GetMethod(
"RegisterHandlers",
BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Static,
binder: null,
[
typeof(IIocContainer),
typeof(IEnumerable<Assembly>),
typeof(ILogger)
],
modifiers: null)
?? throw new InvalidOperationException(
"Failed to locate CqrsHandlerRegistrar.RegisterHandlers.");
/// <summary>
/// 为裸测试容器补齐默认 CQRS runtime seam。
/// </summary>
/// <param name="container">目标测试容器。</param>
/// <exception cref="ArgumentNullException"><paramref name="container" /> 为 <see langword="null" />。</exception>
/// <remarks>
/// 这使仅使用 <see cref="MicrosoftDiContainer" /> 的测试环境也能观察与生产路径一致的 runtime 行为,
/// 而无需完整启动服务模块管理器。
/// 该方法按服务类型执行幂等注册,只会补齐当前容器中尚未接线的 CQRS 基础设施。
/// 若容器里只预注册了正式 <see cref="ICqrsRuntime" /> seam本方法也会补齐旧命名空间下的
/// <see cref="LegacyICqrsRuntime" /> 兼容别名,并保持它与正式 seam 指向同一实例。
/// </remarks>
public static void RegisterInfrastructure(MicrosoftDiContainer container)
{
ArgumentNullException.ThrowIfNull(container);
if (container.Get<ICqrsRuntime>() is null)
{
var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
var notificationPublisher = container.Get<INotificationPublisher>();
var runtime = CqrsRuntimeFactory.CreateRuntime(container, runtimeLogger, notificationPublisher);
container.Register(runtime);
RegisterLegacyRuntimeAlias(container, runtime);
}
else if (container.Get<LegacyICqrsRuntime>() is null)
{
RegisterLegacyRuntimeAlias(container, container.GetRequired<ICqrsRuntime>());
}
if (container.Get<ICqrsHandlerRegistrar>() is null)
{
var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsHandlerRegistrar");
var registrar = CqrsRuntimeFactory.CreateHandlerRegistrar(container, registrarLogger);
container.Register<ICqrsHandlerRegistrar>(registrar);
}
if (container.Get<ICqrsRegistrationService>() is null)
{
var registrationLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsRegistrationService");
var registrar = container.GetRequired<ICqrsHandlerRegistrar>();
var registrationService = CqrsRuntimeFactory.CreateRegistrationService(registrar, registrationLogger);
container.Register<ICqrsRegistrationService>(registrationService);
}
}
/// <summary>
/// 为旧命名空间下的 CQRS runtime 契约注册兼容别名。
/// </summary>
/// <param name="container">承载运行时实例的测试容器。</param>
/// <param name="runtime">当前正式 CQRS runtime 实例。</param>
/// <remarks>
/// 测试辅助层显式保留这条 helper避免“已存在正式 seam 时再补旧别名”的兼容语义分散在多个分支里。
/// </remarks>
private static void RegisterLegacyRuntimeAlias(MicrosoftDiContainer container, ICqrsRuntime runtime)
{
container.Register<LegacyICqrsRuntime>((LegacyICqrsRuntime)runtime);
}
/// <summary>
/// 通过与生产代码一致的注册入口扫描并注册指定程序集中的 CQRS 处理器。
/// </summary>
/// <param name="container">承载处理器映射的测试容器。</param>
/// <param name="assemblies">要扫描的程序集集合。</param>
/// <exception cref="ArgumentNullException">
/// <paramref name="container" /> 或 <paramref name="assemblies" /> 为 <see langword="null" />。
/// </exception>
/// <exception cref="TargetInvocationException">反射调用底层 CQRS 处理器注册入口失败时抛出。</exception>
/// <remarks>
/// 该入口会自动调用 <see cref="RegisterInfrastructure" />,因此测试通常无需预先手动接线 CQRS 基础设施。
/// 程序集去重与空元素过滤由生产注册入口统一处理,避免测试辅助层复制相同筛选逻辑。
/// </remarks>
public static void RegisterHandlers(MicrosoftDiContainer container, params Assembly[] assemblies)
{
ArgumentNullException.ThrowIfNull(container);
ArgumentNullException.ThrowIfNull(assemblies);
RegisterInfrastructure(container);
var logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
RegisterHandlersMethod.Invoke(
null,
[container, assemblies, logger]);
}
}