GFramework/GFramework.Core/Cqrs/LegacyCqrsDispatchHelper.cs
gewuyou dc3bd3744e fix(core): 收口 legacy bridge 同步评审问题
- 修复 legacy 同步 bridge 的 runtime 等待方式,统一通过共享 helper 隔离同步上下文并收口重复 dispatch-context 解析逻辑

- 补充 legacy async command bridge 的取消可见性,并更新 ICqrsRuntime 与相关入口的契约说明

- 新增 bridge 回归测试并更新 cqrs-rewrite active tracking,覆盖同步上下文隔离、测试容器释放与取消语义
2026-05-07 19:00:49 +08:00

95 lines
3.7 KiB
C#

// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs;
/// <summary>
/// 为 legacy Core CQRS bridge 提供共享的上下文解析与同步兼容辅助逻辑。
/// </summary>
/// <remarks>
/// 旧的同步 Command/Query 入口仍需要阻塞等待统一 <see cref="ICqrsRuntime" /> 返回结果。
/// 这里统一通过 <see cref="Task.Run(System.Func{System.Threading.Tasks.Task})" /> 把等待动作切换到线程池,
/// 避免直接占用调用方的 <see cref="SynchronizationContext" /> 导致 legacy 同步入口与异步 pipeline 互相卡死。
/// </remarks>
internal static class LegacyCqrsDispatchHelper
{
/// <summary>
/// 解析当前 legacy 目标对象是否能够绑定到统一 CQRS runtime 的架构上下文。
/// </summary>
/// <param name="runtime">当前执行器可用的统一 CQRS runtime。</param>
/// <param name="target">即将执行的 legacy 目标对象。</param>
/// <param name="context">命中时返回可用于 CQRS runtime 的架构上下文。</param>
/// <returns>
/// 当 <paramref name="runtime" /> 可用且 <paramref name="target" /> 能稳定提供
/// <see cref="IArchitectureContext" /> 时返回 <see langword="true" />;否则返回 <see langword="false" />。
/// </returns>
internal static bool TryResolveDispatchContext(
[NotNullWhen(true)] ICqrsRuntime? runtime,
object target,
out IArchitectureContext context)
{
ArgumentNullException.ThrowIfNull(target);
context = null!;
if (runtime is null || target is not IContextAware contextAware)
{
return false;
}
try
{
context = contextAware.GetContext();
return true;
}
catch (InvalidOperationException)
{
return false;
}
}
/// <summary>
/// 同步等待统一 CQRS runtime 完成无返回值请求。
/// </summary>
/// <param name="runtime">负责分发当前请求的统一 CQRS runtime。</param>
/// <param name="context">当前架构上下文。</param>
/// <param name="request">要同步等待的请求。</param>
internal static void SendSynchronously(
ICqrsRuntime runtime,
IArchitectureContext context,
IRequest<Unit> request)
{
ArgumentNullException.ThrowIfNull(runtime);
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(request);
Task.Run(() => runtime.SendAsync(context, request).AsTask()).GetAwaiter().GetResult();
}
/// <summary>
/// 同步等待统一 CQRS runtime 完成带返回值请求,并返回实际响应。
/// </summary>
/// <typeparam name="TResponse">请求响应类型。</typeparam>
/// <param name="runtime">负责分发当前请求的统一 CQRS runtime。</param>
/// <param name="context">当前架构上下文。</param>
/// <param name="request">要同步等待的请求。</param>
/// <returns>统一 CQRS runtime 返回的响应结果。</returns>
internal static TResponse SendSynchronously<TResponse>(
ICqrsRuntime runtime,
IArchitectureContext context,
IRequest<TResponse> request)
{
ArgumentNullException.ThrowIfNull(runtime);
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(request);
return Task.Run(() => runtime.SendAsync(context, request).AsTask()).GetAwaiter().GetResult();
}
}