mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 00:39:00 +08:00
fix(cqrs): 收口PR审查遗留问题
- 修复并发 CQRS 解析测试的失败路径释放逻辑,并收敛重复 orchestration 以消除新增 analyzer warning - 更新 generated request invoker provider 相关测试、XML 文档与 generator 注释,明确默认 runtime 的描述符预热契约 - 调整 legacy runtime alias 注册与 generated provider 注册顺序,并同步 cqrs-rewrite 跟踪文档中的 PR #305 triage 结果
This commit is contained in:
parent
0c65cd8e38
commit
0f1e91a499
@ -306,8 +306,6 @@ public class ArchitectureContextTests
|
|||||||
public async Task SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently()
|
public async Task SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently()
|
||||||
{
|
{
|
||||||
const int workerCount = 8;
|
const int workerCount = 8;
|
||||||
var workerStartupTimeout = TimeSpan.FromSeconds(5);
|
|
||||||
var firstResolutionTimeout = TimeSpan.FromSeconds(5);
|
|
||||||
using var startGate = new ManualResetEventSlim(false);
|
using var startGate = new ManualResetEventSlim(false);
|
||||||
using var allowResolutionToComplete = new ManualResetEventSlim(false);
|
using var allowResolutionToComplete = new ManualResetEventSlim(false);
|
||||||
using var workersReady = new CountdownEvent(workerCount);
|
using var workersReady = new CountdownEvent(workerCount);
|
||||||
@ -339,18 +337,11 @@ public class ArchitectureContextTests
|
|||||||
}))
|
}))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
Assert.That(
|
ReleaseWorkersAfterFirstResolutionAttempt(
|
||||||
workersReady.Wait(workerStartupTimeout),
|
workersReady,
|
||||||
Is.True,
|
startGate,
|
||||||
"Expected all workers to be ready before releasing start gate.");
|
allowResolutionToComplete,
|
||||||
startGate.Set();
|
() => Volatile.Read(ref resolutionCallCount) > 0);
|
||||||
|
|
||||||
Assert.That(
|
|
||||||
SpinWait.SpinUntil(() => Volatile.Read(ref resolutionCallCount) > 0, firstResolutionTimeout),
|
|
||||||
Is.True,
|
|
||||||
"Expected at least one CQRS runtime resolution attempt.");
|
|
||||||
|
|
||||||
allowResolutionToComplete.Set();
|
|
||||||
|
|
||||||
var responses = await Task.WhenAll(requests);
|
var responses = await Task.WhenAll(requests);
|
||||||
|
|
||||||
@ -372,8 +363,6 @@ public class ArchitectureContextTests
|
|||||||
public async Task PublishAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently()
|
public async Task PublishAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently()
|
||||||
{
|
{
|
||||||
const int workerCount = 8;
|
const int workerCount = 8;
|
||||||
var workerStartupTimeout = TimeSpan.FromSeconds(5);
|
|
||||||
var firstResolutionTimeout = TimeSpan.FromSeconds(5);
|
|
||||||
using var startGate = new ManualResetEventSlim(false);
|
using var startGate = new ManualResetEventSlim(false);
|
||||||
using var allowResolutionToComplete = new ManualResetEventSlim(false);
|
using var allowResolutionToComplete = new ManualResetEventSlim(false);
|
||||||
using var workersReady = new CountdownEvent(workerCount);
|
using var workersReady = new CountdownEvent(workerCount);
|
||||||
@ -405,18 +394,11 @@ public class ArchitectureContextTests
|
|||||||
}))
|
}))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
Assert.That(
|
ReleaseWorkersAfterFirstResolutionAttempt(
|
||||||
workersReady.Wait(workerStartupTimeout),
|
workersReady,
|
||||||
Is.True,
|
startGate,
|
||||||
"Expected all workers to be ready before releasing start gate.");
|
allowResolutionToComplete,
|
||||||
startGate.Set();
|
() => Volatile.Read(ref resolutionCallCount) > 0);
|
||||||
|
|
||||||
Assert.That(
|
|
||||||
SpinWait.SpinUntil(() => Volatile.Read(ref resolutionCallCount) > 0, firstResolutionTimeout),
|
|
||||||
Is.True,
|
|
||||||
"Expected at least one CQRS runtime resolution attempt.");
|
|
||||||
|
|
||||||
allowResolutionToComplete.Set();
|
|
||||||
|
|
||||||
await Task.WhenAll(notifications).ConfigureAwait(false);
|
await Task.WhenAll(notifications).ConfigureAwait(false);
|
||||||
|
|
||||||
@ -437,8 +419,6 @@ public class ArchitectureContextTests
|
|||||||
public async Task CreateStream_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently()
|
public async Task CreateStream_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently()
|
||||||
{
|
{
|
||||||
const int workerCount = 8;
|
const int workerCount = 8;
|
||||||
var workerStartupTimeout = TimeSpan.FromSeconds(5);
|
|
||||||
var firstResolutionTimeout = TimeSpan.FromSeconds(5);
|
|
||||||
using var startGate = new ManualResetEventSlim(false);
|
using var startGate = new ManualResetEventSlim(false);
|
||||||
using var allowResolutionToComplete = new ManualResetEventSlim(false);
|
using var allowResolutionToComplete = new ManualResetEventSlim(false);
|
||||||
using var workersReady = new CountdownEvent(workerCount);
|
using var workersReady = new CountdownEvent(workerCount);
|
||||||
@ -470,18 +450,11 @@ public class ArchitectureContextTests
|
|||||||
}))
|
}))
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
Assert.That(
|
ReleaseWorkersAfterFirstResolutionAttempt(
|
||||||
workersReady.Wait(workerStartupTimeout),
|
workersReady,
|
||||||
Is.True,
|
startGate,
|
||||||
"Expected all workers to be ready before releasing start gate.");
|
allowResolutionToComplete,
|
||||||
startGate.Set();
|
() => Volatile.Read(ref resolutionCallCount) > 0);
|
||||||
|
|
||||||
Assert.That(
|
|
||||||
SpinWait.SpinUntil(() => Volatile.Read(ref resolutionCallCount) > 0, firstResolutionTimeout),
|
|
||||||
Is.True,
|
|
||||||
"Expected at least one CQRS runtime resolution attempt.");
|
|
||||||
|
|
||||||
allowResolutionToComplete.Set();
|
|
||||||
|
|
||||||
await Task.WhenAll(streamTasks).ConfigureAwait(false);
|
await Task.WhenAll(streamTasks).ConfigureAwait(false);
|
||||||
|
|
||||||
@ -509,6 +482,46 @@ public class ArchitectureContextTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放并发 worker,并确保在断言失败时也能放行首次 runtime 解析。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="workersReady">用于确认 worker 已就绪的倒计时器。</param>
|
||||||
|
/// <param name="startGate">用于同时放行 worker 的门闩。</param>
|
||||||
|
/// <param name="allowResolutionToComplete">用于解除首次 runtime 解析阻塞的门闩。</param>
|
||||||
|
/// <param name="hasObservedResolutionAttempt">用于判断当前是否已观察到首次 runtime 解析尝试。</param>
|
||||||
|
private static void ReleaseWorkersAfterFirstResolutionAttempt(
|
||||||
|
CountdownEvent workersReady,
|
||||||
|
ManualResetEventSlim startGate,
|
||||||
|
ManualResetEventSlim allowResolutionToComplete,
|
||||||
|
Func<bool> hasObservedResolutionAttempt)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(workersReady);
|
||||||
|
ArgumentNullException.ThrowIfNull(startGate);
|
||||||
|
ArgumentNullException.ThrowIfNull(allowResolutionToComplete);
|
||||||
|
ArgumentNullException.ThrowIfNull(hasObservedResolutionAttempt);
|
||||||
|
|
||||||
|
var workerStartupTimeout = TimeSpan.FromSeconds(5);
|
||||||
|
var firstResolutionTimeout = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
Assert.That(
|
||||||
|
workersReady.Wait(workerStartupTimeout),
|
||||||
|
Is.True,
|
||||||
|
"Expected all workers to be ready before releasing start gate.");
|
||||||
|
startGate.Set();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Assert.That(
|
||||||
|
SpinWait.SpinUntil(hasObservedResolutionAttempt, firstResolutionTimeout),
|
||||||
|
Is.True,
|
||||||
|
"Expected at least one CQRS runtime resolution attempt.");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
allowResolutionToComplete.Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 为 `CreateStream` 并发解析测试提供最小异步流。
|
/// 为 `CreateStream` 并发解析测试提供最小异步流。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -67,6 +67,23 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
|||||||
requestInvokerEmissions);
|
requestInvokerEmissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从 direct handler 注册描述中提取 request invoker 发射计划。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="supportsRequestInvokerProvider">
|
||||||
|
/// 指示当前 runtime 是否同时暴露 <c>ICqrsRequestInvokerProvider</c> 与
|
||||||
|
/// <c>IEnumeratesCqrsRequestInvokerDescriptors</c> 契约;若不支持,则本方法必须返回空结果并让后续发射路径整体跳过。
|
||||||
|
/// </param>
|
||||||
|
/// <param name="registrations">已按稳定顺序整理完成的 handler 注册描述。</param>
|
||||||
|
/// <returns>
|
||||||
|
/// 由 <c>directRegistration.RequestInvokerRegistration</c> 派生出的 <see cref="RequestInvokerEmissionSpec" /> 集合。
|
||||||
|
/// <c>methodIndex</c> 按 <paramref name="registrations" /> 与其 direct registration 的遍历顺序单调递增,
|
||||||
|
/// 因而只要上游排序稳定,生成的 invoker 方法名与描述符顺序就跨运行保持稳定。
|
||||||
|
/// </returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// 缺少 <c>RequestInvokerRegistration</c> 的 direct registration 会被显式跳过,而不会生成半成品 provider 成员;
|
||||||
|
/// 调用方应把“为什么没有 request invoker registration”对应的诊断留在更早的建模阶段,而不是在源码发射阶段兜底。
|
||||||
|
/// </remarks>
|
||||||
private static ImmutableArray<RequestInvokerEmissionSpec> CreateRequestInvokerEmissions(
|
private static ImmutableArray<RequestInvokerEmissionSpec> CreateRequestInvokerEmissions(
|
||||||
bool supportsRequestInvokerProvider,
|
bool supportsRequestInvokerProvider,
|
||||||
IReadOnlyList<ImplementationRegistrationSpec> registrations)
|
IReadOnlyList<ImplementationRegistrationSpec> registrations)
|
||||||
@ -273,6 +290,17 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
|||||||
builder.AppendLine(" }");
|
builder.AppendLine(" }");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发射 generated registry 的 request invoker provider 成员。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">生成源码构造器。</param>
|
||||||
|
/// <param name="requestInvokerEmissions">
|
||||||
|
/// 来自 <see cref="CreateRequestInvokerEmissions(bool, IReadOnlyList{ImplementationRegistrationSpec})" /> 的稳定发射计划。
|
||||||
|
/// </param>
|
||||||
|
/// <remarks>
|
||||||
|
/// 该输出包含三部分:描述符数组、provider 查询方法,以及与描述符逐项对应的静态 invoker 方法。
|
||||||
|
/// 若发射计划为空,调用方应直接跳过整个 provider 分支,而不是输出空的 registry seam。
|
||||||
|
/// </remarks>
|
||||||
private static void AppendRequestInvokerProviderMembers(
|
private static void AppendRequestInvokerProviderMembers(
|
||||||
StringBuilder builder,
|
StringBuilder builder,
|
||||||
ImmutableArray<RequestInvokerEmissionSpec> requestInvokerEmissions)
|
ImmutableArray<RequestInvokerEmissionSpec> requestInvokerEmissions)
|
||||||
@ -288,6 +316,15 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发射 generated registry 的 request invoker 描述符数组。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">生成源码构造器。</param>
|
||||||
|
/// <param name="requestInvokerEmissions">当前要输出的 request invoker 发射计划。</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// 每个条目都会把请求类型、响应类型和对应的静态 invoker 方法打包成
|
||||||
|
/// <c>CqrsRequestInvokerDescriptorEntry</c>,供 registrar 在注册阶段写入 dispatcher 的弱缓存。
|
||||||
|
/// </remarks>
|
||||||
private static void AppendRequestInvokerDescriptorArray(
|
private static void AppendRequestInvokerDescriptorArray(
|
||||||
StringBuilder builder,
|
StringBuilder builder,
|
||||||
ImmutableArray<RequestInvokerEmissionSpec> requestInvokerEmissions)
|
ImmutableArray<RequestInvokerEmissionSpec> requestInvokerEmissions)
|
||||||
@ -319,6 +356,14 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
|||||||
builder.AppendLine(" ];");
|
builder.AppendLine(" ];");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发射 generated registry 对 request invoker provider 契约的实现方法。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">生成源码构造器。</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// 默认 runtime 真正消费的是 <c>GetDescriptors()</c> 暴露的完整描述符集合,并在注册阶段一次性写入缓存;
|
||||||
|
/// <c>TryGetDescriptor(...)</c> 保留为显式查询接口,因此这里使用线性扫描即可保持生成代码简单且无额外字典分配。
|
||||||
|
/// </remarks>
|
||||||
private static void AppendRequestInvokerProviderMethods(StringBuilder builder)
|
private static void AppendRequestInvokerProviderMethods(StringBuilder builder)
|
||||||
{
|
{
|
||||||
builder.Append(" public global::System.Collections.Generic.IReadOnlyList<global::");
|
builder.Append(" public global::System.Collections.Generic.IReadOnlyList<global::");
|
||||||
@ -351,6 +396,15 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
|||||||
builder.AppendLine(" }");
|
builder.AppendLine(" }");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为单个 request invoker 描述符发射对应的静态强类型桥接方法。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">生成源码构造器。</param>
|
||||||
|
/// <param name="emission">当前要输出的 invoker 发射计划。</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// 这些方法的编号与 <see cref="RequestInvokerEmissionSpec.MethodIndex" /> 一一对应,
|
||||||
|
/// dispatcher 通过描述符里的 <see cref="MethodInfo" /> 把 object 形参桥接回强类型 handler 与 request。
|
||||||
|
/// </remarks>
|
||||||
private static void AppendRequestInvokerMethod(StringBuilder builder, RequestInvokerEmissionSpec emission)
|
private static void AppendRequestInvokerMethod(StringBuilder builder, RequestInvokerEmissionSpec emission)
|
||||||
{
|
{
|
||||||
builder.Append(" private static global::System.Threading.Tasks.ValueTask<");
|
builder.Append(" private static global::System.Threading.Tasks.ValueTask<");
|
||||||
|
|||||||
@ -62,7 +62,7 @@ internal sealed class CqrsArchitectureContextAdvancedFeaturesTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Request_With_Retry_Behavior_Should_Retry_On_Failure()
|
public async Task Request_With_Retry_Behavior_Should_Succeed_On_First_Attempt()
|
||||||
{
|
{
|
||||||
// 由于我们没有实现实际的重试行为,简化测试逻辑
|
// 由于我们没有实现实际的重试行为,简化测试逻辑
|
||||||
TestRetryBehavior.AttemptCount = 0;
|
TestRetryBehavior.AttemptCount = 0;
|
||||||
@ -132,7 +132,7 @@ internal sealed class CqrsArchitectureContextAdvancedFeaturesTests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Transient_Error_Should_Be_Handled_By_Retry_Mechanism()
|
public async Task Transient_Error_Request_Should_Succeed_Without_Simulated_Errors()
|
||||||
{
|
{
|
||||||
// 由于我们没有实现实际的瞬态错误处理,简化测试逻辑
|
// 由于我们没有实现实际的瞬态错误处理,简化测试逻辑
|
||||||
TestTransientErrorHandler.ErrorCount = 0;
|
TestTransientErrorHandler.ErrorCount = 0;
|
||||||
|
|||||||
@ -67,8 +67,7 @@ public class CqrsArchitectureContextIntegrationTests
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task Handler_Can_Access_Architecture_Context()
|
public async Task Handler_Can_Access_Architecture_Context()
|
||||||
{
|
{
|
||||||
// 当前测试通过直接注入上下文来聚焦验证架构上下文集成结果。
|
TestContextAwareHandler.LastContext = null;
|
||||||
TestContextAwareHandler.LastContext = _context;
|
|
||||||
var request = new TestContextAwareRequest();
|
var request = new TestContextAwareRequest();
|
||||||
|
|
||||||
await _context!.SendRequestAsync(request).ConfigureAwait(false);
|
await _context!.SendRequestAsync(request).ConfigureAwait(false);
|
||||||
@ -359,17 +358,17 @@ public class CqrsArchitectureContextIntegrationTests
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 为上下文感知请求提供静态响应的测试处理器。
|
/// 为上下文感知请求提供静态响应的测试处理器。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class TestContextAwareRequestHandler : IRequestHandler<TestContextAwareRequest, string>
|
public sealed class TestContextAwareRequestHandler : ContextAwareBase, IRequestHandler<TestContextAwareRequest, string>
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 处理请求并返回固定结果。
|
/// 记录当前处理器观察到的架构上下文,并返回固定结果。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="request">当前测试请求。</param>
|
/// <param name="request">当前测试请求。</param>
|
||||||
/// <param name="cancellationToken">取消令牌。</param>
|
/// <param name="cancellationToken">取消令牌。</param>
|
||||||
/// <returns>固定的测试结果。</returns>
|
/// <returns>固定的测试结果。</returns>
|
||||||
public ValueTask<string> Handle(TestContextAwareRequest request, CancellationToken cancellationToken)
|
public ValueTask<string> Handle(TestContextAwareRequest request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
// 保持测试中设置的上下文,不要重置为空。
|
TestContextAwareHandler.LastContext = Context;
|
||||||
return new ValueTask<string>("Context accessed");
|
return new ValueTask<string>("Context accessed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,12 +14,15 @@ namespace GFramework.Cqrs.Tests.Cqrs;
|
|||||||
[NonParallelizable]
|
[NonParallelizable]
|
||||||
internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||||
{
|
{
|
||||||
|
private ILoggerFactoryProvider? _previousLoggerFactoryProvider;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在每个用例前重置 registrar / dispatcher 的静态缓存,避免跨用例共享状态影响断言。
|
/// 在每个用例前重置 registrar / dispatcher 的静态缓存,避免跨用例共享状态影响断言。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp()
|
public void SetUp()
|
||||||
{
|
{
|
||||||
|
_previousLoggerFactoryProvider = LoggerFactoryResolver.Provider;
|
||||||
LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
|
LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
|
||||||
ClearRegistrarCaches();
|
ClearRegistrarCaches();
|
||||||
ClearDispatcherCaches();
|
ClearDispatcherCaches();
|
||||||
@ -31,6 +34,7 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
|||||||
[TearDown]
|
[TearDown]
|
||||||
public void TearDown()
|
public void TearDown()
|
||||||
{
|
{
|
||||||
|
LoggerFactoryResolver.Provider = _previousLoggerFactoryProvider ?? new ConsoleLoggerFactoryProvider();
|
||||||
ClearRegistrarCaches();
|
ClearRegistrarCaches();
|
||||||
ClearDispatcherCaches();
|
ClearDispatcherCaches();
|
||||||
}
|
}
|
||||||
@ -67,18 +71,7 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
|||||||
|
|
||||||
var context = new ArchitectureContext(container);
|
var context = new ArchitectureContext(container);
|
||||||
var response = await context.SendRequestAsync(new GeneratedRequestInvokerRequest("payload"));
|
var response = await context.SendRequestAsync(new GeneratedRequestInvokerRequest("payload"));
|
||||||
|
Assert.That(response, Is.EqualTo("generated:payload"));
|
||||||
var requestBindings = GetDispatcherCacheField("RequestDispatchBindings");
|
|
||||||
var binding = GetRequestDispatchBindingValue(
|
|
||||||
requestBindings,
|
|
||||||
typeof(GeneratedRequestInvokerRequest),
|
|
||||||
typeof(string));
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
|
||||||
{
|
|
||||||
Assert.That(response, Is.EqualTo("generated:payload"));
|
|
||||||
Assert.That(binding, Is.Not.Null);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -156,22 +149,4 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
|||||||
.Invoke(cache, Array.Empty<object>());
|
.Invoke(cache, Array.Empty<object>());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 读取指定请求/响应类型对当前缓存的 request dispatch binding。
|
|
||||||
/// </summary>
|
|
||||||
private static object? GetRequestDispatchBindingValue(object requestBindings, Type requestType, Type responseType)
|
|
||||||
{
|
|
||||||
var bindingBox = requestBindings.GetType()
|
|
||||||
.GetMethod("GetValueOrDefaultForTesting", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)!
|
|
||||||
.Invoke(requestBindings, [requestType, responseType]);
|
|
||||||
if (bindingBox is null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bindingBox.GetType()
|
|
||||||
.GetMethod("Get", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)!
|
|
||||||
.MakeGenericMethod(responseType)
|
|
||||||
.Invoke(bindingBox, Array.Empty<object>());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,4 +5,5 @@ namespace GFramework.Cqrs.Tests.Cqrs;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用于验证 generated request invoker provider 接线的测试请求。
|
/// 用于验证 generated request invoker provider 接线的测试请求。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="Value">用于验证 generated invoker 结果拼接的请求负载。</param>
|
||||||
internal sealed record GeneratedRequestInvokerRequest(string Value) : IRequest<string>;
|
internal sealed record GeneratedRequestInvokerRequest(string Value) : IRequest<string>;
|
||||||
|
|||||||
@ -9,16 +9,23 @@ namespace GFramework.Cqrs;
|
|||||||
/// 该 seam 允许运行时在首次创建 request dispatch binding 时,
|
/// 该 seam 允许运行时在首次创建 request dispatch binding 时,
|
||||||
/// 直接复用编译期已知的请求/响应类型映射,而不是总是通过反射闭合泛型方法生成调用委托。
|
/// 直接复用编译期已知的请求/响应类型映射,而不是总是通过反射闭合泛型方法生成调用委托。
|
||||||
/// 当当前程序集没有提供匹配项时,dispatcher 仍会回退到既有的反射绑定创建路径。
|
/// 当当前程序集没有提供匹配项时,dispatcher 仍会回退到既有的反射绑定创建路径。
|
||||||
|
/// 当前默认 runtime 通过 <see cref="IEnumeratesCqrsRequestInvokerDescriptors" /> 在注册阶段一次性读取并缓存
|
||||||
|
/// provider 暴露的描述符;<see cref="TryGetDescriptor(Type, Type, out CqrsRequestInvokerDescriptor?)" />
|
||||||
|
/// 主要用于 provider 自检、测试和显式调用场景,而不是 dispatcher 在分发热路径上的二次回调入口。
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public interface ICqrsRequestInvokerProvider
|
public interface ICqrsRequestInvokerProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 尝试为指定请求/响应类型对提供运行时元数据。
|
/// 尝试为指定请求/响应类型对提供运行时元数据。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requestType">请求运行时类型。</param>
|
/// <param name="requestType">请求运行时类型。</param>
|
||||||
/// <param name="responseType">响应运行时类型。</param>
|
/// <param name="responseType">响应运行时类型。</param>
|
||||||
/// <param name="descriptor">命中时返回的 request invoker 元数据。</param>
|
/// <param name="descriptor">命中时返回的 request invoker 元数据。</param>
|
||||||
/// <returns>若当前 provider 可处理该请求/响应类型对则返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
|
/// <returns>若当前 provider 可处理该请求/响应类型对则返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// 若 provider 希望被默认 runtime 自动接线到 dispatcher 的 generated invoker 缓存中,
|
||||||
|
/// 还必须同时实现 <see cref="IEnumeratesCqrsRequestInvokerDescriptors" />,以便 registrar 在注册阶段枚举全部描述符。
|
||||||
|
/// </remarks>
|
||||||
bool TryGetDescriptor(
|
bool TryGetDescriptor(
|
||||||
Type requestType,
|
Type requestType,
|
||||||
Type responseType,
|
Type responseType,
|
||||||
|
|||||||
@ -263,8 +263,8 @@ internal static class CqrsHandlerRegistrar
|
|||||||
if (registry is not ICqrsRequestInvokerProvider provider)
|
if (registry is not ICqrsRequestInvokerProvider provider)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
services.AddSingleton(typeof(ICqrsRequestInvokerProvider), provider);
|
|
||||||
RegisterGeneratedRequestInvokerDescriptors(provider, assemblyName, logger);
|
RegisterGeneratedRequestInvokerDescriptors(provider, assemblyName, logger);
|
||||||
|
services.AddSingleton(typeof(ICqrsRequestInvokerProvider), provider);
|
||||||
logger.Debug(
|
logger.Debug(
|
||||||
$"Registered CQRS request invoker provider {provider.GetType().FullName} for assembly {assemblyName}.");
|
$"Registered CQRS request invoker provider {provider.GetType().FullName} for assembly {assemblyName}.");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,9 +16,10 @@ public interface INotificationPublisher
|
|||||||
/// 执行一次通知发布。
|
/// 执行一次通知发布。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TNotification">通知类型。</typeparam>
|
/// <typeparam name="TNotification">通知类型。</typeparam>
|
||||||
/// <param name="context">当前发布调用的处理器集合与执行入口。</param>
|
/// <param name="context">当前发布调用的处理器集合与执行入口,不能为空。</param>
|
||||||
/// <param name="cancellationToken">取消令牌。</param>
|
/// <param name="cancellationToken">取消令牌。</param>
|
||||||
/// <returns>表示通知发布完成的值任务。</returns>
|
/// <returns>表示通知发布完成的值任务。</returns>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="context" /> 为 <see langword="null" />。</exception>
|
||||||
ValueTask PublishAsync<TNotification>(
|
ValueTask PublishAsync<TNotification>(
|
||||||
NotificationPublishContext<TNotification> context,
|
NotificationPublishContext<TNotification> context,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
|
|||||||
@ -2251,8 +2251,6 @@ public class CqrsHandlerRegistryGeneratorTests
|
|||||||
var generatorErrors = execution.GeneratorDiagnostics
|
var generatorErrors = execution.GeneratorDiagnostics
|
||||||
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
var generatedSource = execution.GeneratedSources[0].content;
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That(inputCompilationErrors.Select(static diagnostic => diagnostic.Id), Does.Contain("CS0306"));
|
Assert.That(inputCompilationErrors.Select(static diagnostic => diagnostic.Id), Does.Contain("CS0306"));
|
||||||
@ -2260,6 +2258,7 @@ public class CqrsHandlerRegistryGeneratorTests
|
|||||||
Assert.That(generatorErrors, Is.Empty);
|
Assert.That(generatorErrors, Is.Empty);
|
||||||
Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
|
Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
|
||||||
Assert.That(execution.GeneratedSources[0].filename, Is.EqualTo("CqrsHandlerRegistry.g.cs"));
|
Assert.That(execution.GeneratedSources[0].filename, Is.EqualTo("CqrsHandlerRegistry.g.cs"));
|
||||||
|
var generatedSource = execution.GeneratedSources[0].content;
|
||||||
Assert.That(
|
Assert.That(
|
||||||
generatedSource,
|
generatedSource,
|
||||||
Does.Contain(
|
Does.Contain(
|
||||||
@ -2302,8 +2301,6 @@ public class CqrsHandlerRegistryGeneratorTests
|
|||||||
var generatorErrors = execution.GeneratorDiagnostics
|
var generatorErrors = execution.GeneratorDiagnostics
|
||||||
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
var generatedSource = execution.GeneratedSources[0].content;
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That(inputCompilationErrors.Select(static diagnostic => diagnostic.Id), Does.Contain("CS0306"));
|
Assert.That(inputCompilationErrors.Select(static diagnostic => diagnostic.Id), Does.Contain("CS0306"));
|
||||||
@ -2311,6 +2308,7 @@ public class CqrsHandlerRegistryGeneratorTests
|
|||||||
Assert.That(generatorErrors, Is.Empty);
|
Assert.That(generatorErrors, Is.Empty);
|
||||||
Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
|
Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
|
||||||
Assert.That(execution.GeneratedSources[0].filename, Is.EqualTo("CqrsHandlerRegistry.g.cs"));
|
Assert.That(execution.GeneratedSources[0].filename, Is.EqualTo("CqrsHandlerRegistry.g.cs"));
|
||||||
|
var generatedSource = execution.GeneratedSources[0].content;
|
||||||
Assert.That(
|
Assert.That(
|
||||||
generatedSource,
|
generatedSource,
|
||||||
Does.Contain(
|
Does.Contain(
|
||||||
@ -2358,8 +2356,6 @@ public class CqrsHandlerRegistryGeneratorTests
|
|||||||
var generatorErrors = execution.GeneratorDiagnostics
|
var generatorErrors = execution.GeneratorDiagnostics
|
||||||
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
var generatedSource = execution.GeneratedSources[0].content;
|
|
||||||
|
|
||||||
Assert.Multiple(() =>
|
Assert.Multiple(() =>
|
||||||
{
|
{
|
||||||
Assert.That(inputCompilationErrors, Is.Empty);
|
Assert.That(inputCompilationErrors, Is.Empty);
|
||||||
@ -2367,6 +2363,7 @@ public class CqrsHandlerRegistryGeneratorTests
|
|||||||
Assert.That(generatorErrors, Is.Empty);
|
Assert.That(generatorErrors, Is.Empty);
|
||||||
Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
|
Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
|
||||||
Assert.That(execution.GeneratedSources[0].filename, Is.EqualTo("CqrsHandlerRegistry.g.cs"));
|
Assert.That(execution.GeneratedSources[0].filename, Is.EqualTo("CqrsHandlerRegistry.g.cs"));
|
||||||
|
var generatedSource = execution.GeneratedSources[0].content;
|
||||||
Assert.That(
|
Assert.That(
|
||||||
generatedSource,
|
generatedSource,
|
||||||
Does.Contain(
|
Does.Contain(
|
||||||
|
|||||||
@ -98,7 +98,13 @@ public static class CqrsTestRuntime
|
|||||||
/// </remarks>
|
/// </remarks>
|
||||||
private static void RegisterLegacyRuntimeAlias(MicrosoftDiContainer container, ICqrsRuntime runtime)
|
private static void RegisterLegacyRuntimeAlias(MicrosoftDiContainer container, ICqrsRuntime runtime)
|
||||||
{
|
{
|
||||||
container.Register<LegacyICqrsRuntime>((LegacyICqrsRuntime)runtime);
|
if (runtime is not LegacyICqrsRuntime legacyRuntime)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"The registered {nameof(ICqrsRuntime)} must also implement {typeof(LegacyICqrsRuntime).FullName}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
container.Register<LegacyICqrsRuntime>(legacyRuntime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -123,10 +123,11 @@ CQRS 迁移与收敛。
|
|||||||
- 本地核对后确认 `dotnet-format` 仍只有 `Restore operation failed` 噪音,没有附带当前仍成立的文件级格式诊断
|
- 本地核对后确认 `dotnet-format` 仍只有 `Restore operation failed` 噪音,没有附带当前仍成立的文件级格式诊断
|
||||||
- 已按 review triage 修正 generator source preamble 的多实例 fallback 特性排版、移除死参数,并补强 mixed/direct fallback 发射回归断言与 XML 文档
|
- 已按 review triage 修正 generator source preamble 的多实例 fallback 特性排版、移除死参数,并补强 mixed/direct fallback 发射回归断言与 XML 文档
|
||||||
- `2026-04-30` 已重新执行 `$gframework-pr-review`:
|
- `2026-04-30` 已重新执行 `$gframework-pr-review`:
|
||||||
- 当前分支对应 `PR #304`,状态为 `OPEN`
|
- 当前分支对应 `PR #305`,状态为 `OPEN`
|
||||||
- latest reviewed commit 当前剩余 `7` 条 CodeRabbit nitpick 与 `2` 条 Greptile open threads,集中在测试脆弱断言、共享测试状态并发保护,以及 `CqrsDispatcher` 的缓存线程模型文档
|
- 当前抓取到 `9` 条 CodeRabbit open threads、`2` 条 Greptile open threads;远端 CTRF 汇总为 `2214/2214` passed,MegaLinter 仍只暴露 `dotnet-format` 的 `Restore operation failed` 环境噪音
|
||||||
- 本地核对后,已确认这些评论仍对应当前代码;MegaLinter 继续只暴露 `dotnet-format` 的 `Restore operation failed` 环境噪音,CTRF 汇总为 `2203/2203` passed
|
- 本地核对后,已确认以下评论仍然成立并已完成修正:`ArchitectureContextTests` 并发测试失败路径释放、`CqrsGeneratedRequestInvokerProviderTests` 的全局 logger provider 恢复与私有缓存断言解耦、`CqrsArchitectureContextIntegrationTests` 的真实上下文注入断言、`GeneratedRequestInvokerRequest` / `INotificationPublisher` XML 文档、`CqrsHandlerRegistrar` 的 provider 注册顺序、`CqrsTestRuntime` 的 legacy alias 显式失败模式,以及 `cqrs-rewrite` trace 重复标题
|
||||||
- 已在本地完成 follow-up:request pipeline invoker 改为 binding 级复用、共享测试状态切换到 `System.Threading.Lock` 保护、顺序测试改为受控记录接口、`CqrsDispatcherCacheTests` 标记为 `NonParallelizable`,并补齐相关 XML / 线程模型注释
|
- 对于 `ICqrsRequestInvokerProvider` / generated `TryGetDescriptor(...)` 相关 Greptile 评论,本地评估后未改 dispatcher 热路径语义;改为补齐公开注释与生成器方法级注释,明确默认 runtime 只在注册阶段经 `IEnumeratesCqrsRequestInvokerDescriptors` 预热缓存,`TryGetDescriptor(...)` 保留为显式查询 seam
|
||||||
|
- 本轮额外修正了 `GFramework.SourceGenerators.Tests` 中先读取 `GeneratedSources[0]` 再断言长度的脆弱顺序,并将 `ArchitectureContextTests` 的并发 orchestration 收敛到公共 helper,消除本轮引入的 `MA0051` warning
|
||||||
- `2026-04-29` 已完成一轮 precise runtime type lookup 的数组回归补强:
|
- `2026-04-29` 已完成一轮 precise runtime type lookup 的数组回归补强:
|
||||||
- `GFramework.SourceGenerators.Tests` 已新增多维数组、交错数组、外部程序集隐藏元素类型三类回归
|
- `GFramework.SourceGenerators.Tests` 已新增多维数组、交错数组、外部程序集隐藏元素类型三类回归
|
||||||
- 当前生成器在 precise runtime type lookup 下已稳定保留数组秩信息,并递归发射交错数组的 `MakeArrayType()` 链
|
- 当前生成器在 precise runtime type lookup 下已稳定保留数组秩信息,并递归发射交错数组的 `MakeArrayType()` 链
|
||||||
@ -212,10 +213,22 @@ CQRS 迁移与收敛。
|
|||||||
- `RP-046` 至 `RP-062` 的历史验证命令与阶段性结果已移入验证归档,active tracking 只保留当前恢复入口需要的最新验证
|
- `RP-046` 至 `RP-062` 的历史验证命令与阶段性结果已移入验证归档,active tracking 只保留当前恢复入口需要的最新验证
|
||||||
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output /tmp/current-pr-review.json`
|
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output /tmp/current-pr-review.json`
|
||||||
- 结果:通过
|
- 结果:通过
|
||||||
- 备注:确认当前分支对应 `PR #304`,并定位到仍需本地复核的 CodeRabbit / Greptile open thread
|
- 备注:确认当前分支对应 `PR #305`,并定位到仍需本地复核的 CodeRabbit / Greptile open thread
|
||||||
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
||||||
- 结果:通过
|
- 结果:通过
|
||||||
- 备注:`0 warning / 0 error`;本轮确认 XML 文档补齐、`NonParallelizable`、`_syncRoot` 命名与 `ai-plan` 收敛未引入新增编译问题
|
- 备注:`0 warning / 0 error`;本轮确认 XML 文档补齐、`NonParallelizable`、`_syncRoot` 命名与 `ai-plan` 收敛未引入新增编译问题
|
||||||
|
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests|FullyQualifiedName~CqrsArchitectureContextIntegrationTests.Handler_Can_Access_Architecture_Context|FullyQualifiedName~CqrsArchitectureContextAdvancedFeaturesTests.Request_With_Retry_Behavior_Should_Succeed_On_First_Attempt|FullyQualifiedName~CqrsArchitectureContextAdvancedFeaturesTests.Transient_Error_Request_Should_Succeed_Without_Simulated_Errors"`
|
||||||
|
- 结果:通过
|
||||||
|
- 备注:`5/5` passed;覆盖 generated invoker provider、真实上下文注入与两条重命名高级行为测试
|
||||||
|
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently|FullyQualifiedName~PublishAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently|FullyQualifiedName~CreateStream_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently"`
|
||||||
|
- 结果:通过
|
||||||
|
- 备注:`3/3` passed;确认并发首次解析测试在失败路径释放调整后保持通过
|
||||||
|
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~Emits_Request_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available|FullyQualifiedName~Emits_Direct_Type_Fallback_Metadata_When_All_Fallback_Handlers_Are_Referenceable_And_Runtime_Type_Contract_Is_Available|FullyQualifiedName~Emits_Mixed_Direct_Type_And_String_Fallback_Metadata_When_Runtime_Allows_Multiple_Fallback_Attributes"`
|
||||||
|
- 结果:通过
|
||||||
|
- 备注:`3/3` passed;确认 provider 生成分支注释与断言顺序修正未改变生成语义
|
||||||
|
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||||
|
- 结果:通过
|
||||||
|
- 备注:构建成功;并行验证期间出现过 `MSB3026` 拷贝重试噪音,属于同时运行多个 `dotnet` 命令时的输出文件竞争,不是持久性编译 warning
|
||||||
- `bash scripts/validate-csharp-naming.sh`
|
- `bash scripts/validate-csharp-naming.sh`
|
||||||
- 结果:通过
|
- 结果:通过
|
||||||
- 备注:使用显式 `GIT_DIR` / `GIT_WORK_TREE` 绑定重跑后,`1045` 个 tracked C# 文件的命名校验全部通过;本轮 `_syncRoot` 改名未引入命名规则回归
|
- 备注:使用显式 `GIT_DIR` / `GIT_WORK_TREE` 绑定重跑后,`1045` 个 tracked C# 文件的命名校验全部通过;本轮 `_syncRoot` 改名未引入命名规则回归
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
- `CqrsGeneratedRequestInvokerProviderTests` 锁定 registrar 会注册 generated request invoker provider,且 dispatcher 走 generated invoker 后会返回 `generated:` 前缀结果
|
- `CqrsGeneratedRequestInvokerProviderTests` 锁定 registrar 会注册 generated request invoker provider,且 dispatcher 走 generated invoker 后会返回 `generated:` 前缀结果
|
||||||
- `CqrsHandlerRegistryGeneratorTests` 锁定 generated source 会包含 request invoker provider 接口、descriptor 条目与 `InvokeRequestHandler0(...)` 方法
|
- `CqrsHandlerRegistryGeneratorTests` 锁定 generated source 会包含 request invoker provider 接口、descriptor 条目与 `InvokeRequestHandler0(...)` 方法
|
||||||
|
|
||||||
### 验证
|
### 验证(RP-067)
|
||||||
|
|
||||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests|FullyQualifiedName~CqrsHandlerRegistrarTests|FullyQualifiedName~CqrsDispatcherCacheTests"`
|
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests|FullyQualifiedName~CqrsHandlerRegistrarTests|FullyQualifiedName~CqrsDispatcherCacheTests"`
|
||||||
- 结果:通过,`22/22` passed
|
- 结果:通过,`22/22` passed
|
||||||
@ -30,7 +30,7 @@
|
|||||||
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||||
- 结果:通过,`0 warning / 0 error`
|
- 结果:通过,`0 warning / 0 error`
|
||||||
|
|
||||||
### 当前下一步
|
### 当前下一步(RP-067)
|
||||||
|
|
||||||
1. 评估 notification / stream invoker 是否值得沿同一 provider 模式继续前移,或先补 request provider 的公开说明与诊断语义
|
1. 评估 notification / stream invoker 是否值得沿同一 provider 模式继续前移,或先补 request provider 的公开说明与诊断语义
|
||||||
2. 继续在保持 branch diff 低于阈值的前提下推进下一批;当前相对 `origin/main` 的 branch diff 为 `22 files`
|
2. 继续在保持 branch diff 低于阈值的前提下推进下一批;当前相对 `origin/main` 的 branch diff 为 `22 files`
|
||||||
@ -53,14 +53,14 @@
|
|||||||
- `docs/zh-CN/core/cqrs.md`
|
- `docs/zh-CN/core/cqrs.md`
|
||||||
- 三处文档都已明确:`GFramework.Core.Abstractions.Cqrs.ICqrsRuntime` 只是旧命名空间下保留的 compatibility alias,新代码应依赖 `GFramework.Cqrs.Abstractions.Cqrs.ICqrsRuntime`
|
- 三处文档都已明确:`GFramework.Core.Abstractions.Cqrs.ICqrsRuntime` 只是旧命名空间下保留的 compatibility alias,新代码应依赖 `GFramework.Cqrs.Abstractions.Cqrs.ICqrsRuntime`
|
||||||
|
|
||||||
### 验证
|
### 验证(RP-066)
|
||||||
|
|
||||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests"`
|
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests"`
|
||||||
- 结果:通过,`42/42` passed
|
- 结果:通过,`42/42` passed
|
||||||
- `dotnet build GFramework.Core/GFramework.Core.csproj -c Release`
|
- `dotnet build GFramework.Core/GFramework.Core.csproj -c Release`
|
||||||
- 结果:通过,`0 warning / 0 error`
|
- 结果:通过,`0 warning / 0 error`
|
||||||
|
|
||||||
### 当前下一步
|
### 当前下一步(RP-066)
|
||||||
|
|
||||||
1. 在保持 branch diff 低于阈值的前提下,回到 `dispatch/invoker` 生成前移主线
|
1. 在保持 branch diff 低于阈值的前提下,回到 `dispatch/invoker` 生成前移主线
|
||||||
2. 优先尝试只覆盖 request 路径的 generated invoker/provider 最小切片,避免一次卷入 notification / stream / pipeline executor
|
2. 优先尝试只覆盖 request 路径的 generated invoker/provider 最小切片,避免一次卷入 notification / stream / pipeline executor
|
||||||
@ -81,14 +81,14 @@
|
|||||||
- `CreateStream(...)` 在并发首次访问时只解析一次 `ICqrsRuntime`
|
- `CreateStream(...)` 在并发首次访问时只解析一次 `ICqrsRuntime`
|
||||||
- 集成后已确认三份测试文件中不再残留 `GFramework.Cqrs.Tests.Mediator` 命名空间或 `Mediator` 语义命名
|
- 集成后已确认三份测试文件中不再残留 `GFramework.Cqrs.Tests.Mediator` 命名空间或 `Mediator` 语义命名
|
||||||
|
|
||||||
### 验证
|
### 验证(RP-065)
|
||||||
|
|
||||||
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
||||||
- 结果:通过,`0 warning / 0 error`
|
- 结果:通过,`0 warning / 0 error`
|
||||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~ArchitectureContextTests"`
|
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~ArchitectureContextTests"`
|
||||||
- 结果:通过,`22/22` passed
|
- 结果:通过,`22/22` passed
|
||||||
|
|
||||||
### 当前下一步
|
### 当前下一步(RP-065)
|
||||||
|
|
||||||
1. 继续 `Phase 8` 主线,回到 `dispatch/invoker` 生成前移或 `LegacyICqrsRuntime` 收口的下一个低风险切片
|
1. 继续 `Phase 8` 主线,回到 `dispatch/invoker` 生成前移或 `LegacyICqrsRuntime` 收口的下一个低风险切片
|
||||||
2. 在下一次 batch 结束后复算 branch diff,确认距 `50 files` stop condition 的剩余 headroom
|
2. 在下一次 batch 结束后复算 branch diff,确认距 `50 files` stop condition 的剩余 headroom
|
||||||
@ -110,7 +110,7 @@
|
|||||||
- `GFramework.Cqrs/README.md` 与 `docs/zh-CN/core/cqrs.md` 已同步说明默认通知语义与可替换 seam
|
- `GFramework.Cqrs/README.md` 与 `docs/zh-CN/core/cqrs.md` 已同步说明默认通知语义与可替换 seam
|
||||||
- 中途验证曾因并行 .NET 构建产生输出文件锁噪音;已改为串行重跑并获取干净结果
|
- 中途验证曾因并行 .NET 构建产生输出文件锁噪音;已改为串行重跑并获取干净结果
|
||||||
|
|
||||||
### 验证
|
### 验证(RP-064)
|
||||||
|
|
||||||
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||||
- 结果:通过,`0 warning / 0 error`
|
- 结果:通过,`0 warning / 0 error`
|
||||||
@ -123,7 +123,7 @@
|
|||||||
- `GIT_DIR=/mnt/f/gewuyou/System/Documents/WorkSpace/GameDev/GFramework/.git/worktrees/GFramework-cqrs GIT_WORK_TREE=/mnt/f/gewuyou/System/Documents/WorkSpace/GameDev/GFramework-WorkTree/GFramework-cqrs bash scripts/validate-csharp-naming.sh`
|
- `GIT_DIR=/mnt/f/gewuyou/System/Documents/WorkSpace/GameDev/GFramework/.git/worktrees/GFramework-cqrs GIT_WORK_TREE=/mnt/f/gewuyou/System/Documents/WorkSpace/GameDev/GFramework-WorkTree/GFramework-cqrs bash scripts/validate-csharp-naming.sh`
|
||||||
- 结果:通过
|
- 结果:通过
|
||||||
|
|
||||||
### 当前下一步
|
### 当前下一步(RP-064)
|
||||||
|
|
||||||
1. 评估 notification publisher seam 的第二阶段是否需要公开配置面、并行 publisher 或 telemetry decorator
|
1. 评估 notification publisher seam 的第二阶段是否需要公开配置面、并行 publisher 或 telemetry decorator
|
||||||
2. 把 `dispatch/invoker` 生成前移重新拉回 `Phase 8` 主线,作为下一个实现切片
|
2. 把 `dispatch/invoker` 生成前移重新拉回 `Phase 8` 主线,作为下一个实现切片
|
||||||
@ -140,7 +140,7 @@
|
|||||||
- 当前仍未完整吸收 publisher 策略抽象、细粒度 pipeline、telemetry / diagnostics / benchmark 体系与 runtime 主体生成
|
- 当前仍未完整吸收 publisher 策略抽象、细粒度 pipeline、telemetry / diagnostics / benchmark 体系与 runtime 主体生成
|
||||||
- 本轮把默认下一步从“继续盯 PR thread”调整为“围绕 publisher seam 与 dispatch/invoker 生成前移做下一轮设计收敛”
|
- 本轮把默认下一步从“继续盯 PR thread”调整为“围绕 publisher seam 与 dispatch/invoker 生成前移做下一轮设计收敛”
|
||||||
|
|
||||||
### 验证
|
### 验证(RP-063)
|
||||||
|
|
||||||
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||||
- 结果:通过,`0 warning / 0 error`
|
- 结果:通过,`0 warning / 0 error`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user