mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-06 16:16:44 +08:00
fix(cqrs): 收敛生成调用描述符与PR评审回归
- 修复 request 与 stream generated invoker 描述符的静态方法与空值防御,提前拒绝非法元数据 - 补充 provider 空描述符枚举与非静态 invoker 回退回归,更新相关 XML 注释与中文文档语义 - 更新 cqrs-rewrite 活跃跟踪、执行 trace 与验证归档,记录 PR #307 的当前验证结论
This commit is contained in:
parent
8b36626266
commit
83528742bb
@ -226,11 +226,11 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证当 generated request invoker provider 返回实例方法时,
|
||||
/// dispatcher 会显式拒绝该描述符,而不是在后续绑定阶段静默接受非法合同。
|
||||
/// 验证当 generated request invoker provider 暴露实例方法时,
|
||||
/// registrar 会放弃该 generated registry 并回退到运行时反射路径。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void SendAsync_Should_Throw_When_Generated_Request_Invoker_Is_Not_Static()
|
||||
public async Task SendAsync_Should_Fall_Back_To_Runtime_Path_When_Generated_Request_Invoker_Is_Not_Static()
|
||||
{
|
||||
var generatedAssembly = CreateGeneratedAssembly(
|
||||
typeof(NonStaticRequestInvokerProviderRegistry),
|
||||
@ -241,10 +241,8 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
container.Freeze();
|
||||
|
||||
var context = new ArchitectureContext(container);
|
||||
var exception = Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||
await context.SendRequestAsync(new GeneratedRequestInvokerRequest("payload")).ConfigureAwait(false));
|
||||
Assert.That(exception, Is.Not.Null);
|
||||
Assert.That(exception!.Message, Does.Contain("non-static invoker method"));
|
||||
var response = await context.SendRequestAsync(new GeneratedRequestInvokerRequest("payload")).ConfigureAwait(false);
|
||||
Assert.That(response, Is.EqualTo("runtime:payload"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -270,11 +268,11 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证当 generated stream invoker provider 返回实例方法时,
|
||||
/// dispatcher 会在首次建流时显式拒绝该描述符。
|
||||
/// 验证当 generated stream invoker provider 暴露实例方法时,
|
||||
/// registrar 会放弃该 generated registry 并回退到运行时反射路径。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public void CreateStream_Should_Throw_When_Generated_Stream_Invoker_Is_Not_Static()
|
||||
public async Task CreateStream_Should_Fall_Back_To_Runtime_Path_When_Generated_Stream_Invoker_Is_Not_Static()
|
||||
{
|
||||
var generatedAssembly = CreateGeneratedAssembly(
|
||||
typeof(NonStaticStreamInvokerProviderRegistry),
|
||||
@ -285,10 +283,8 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
container.Freeze();
|
||||
|
||||
var context = new ArchitectureContext(container);
|
||||
var exception = Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||
await DrainAsync(context.CreateStream(new GeneratedStreamInvokerRequest(3))).ConfigureAwait(false));
|
||||
Assert.That(exception, Is.Not.Null);
|
||||
Assert.That(exception!.Message, Does.Contain("non-static invoker method"));
|
||||
var results = await DrainAsync(context.CreateStream(new GeneratedStreamInvokerRequest(3))).ConfigureAwait(false);
|
||||
Assert.That(results, Is.EqualTo([3, 4]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -313,6 +309,46 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
Assert.That(exception!.Message, Does.Contain("incompatible invoker"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证当 generated request invoker provider 实现枚举契约、但返回空描述符集合时,
|
||||
/// dispatcher 仍会回退到既有反射路径。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SendAsync_Should_Fall_Back_To_Runtime_Path_When_Request_Descriptor_Enumeration_Is_Empty()
|
||||
{
|
||||
var generatedAssembly = CreateGeneratedAssembly(
|
||||
typeof(EmptyEnumeratingRequestInvokerProviderRegistry),
|
||||
"GFramework.Cqrs.Tests.Cqrs.EmptyEnumeratingRequestInvokerAssembly, Version=1.0.0.0");
|
||||
var container = new MicrosoftDiContainer();
|
||||
|
||||
CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object);
|
||||
container.Freeze();
|
||||
|
||||
var context = new ArchitectureContext(container);
|
||||
var response = await context.SendRequestAsync(new GeneratedRequestInvokerRequest("payload")).ConfigureAwait(false);
|
||||
Assert.That(response, Is.EqualTo("runtime:payload"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证当 generated stream invoker provider 实现枚举契约、但返回空描述符集合时,
|
||||
/// dispatcher 仍会回退到既有流式反射路径。
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task CreateStream_Should_Fall_Back_To_Runtime_Path_When_Stream_Descriptor_Enumeration_Is_Empty()
|
||||
{
|
||||
var generatedAssembly = CreateGeneratedAssembly(
|
||||
typeof(EmptyEnumeratingStreamInvokerProviderRegistry),
|
||||
"GFramework.Cqrs.Tests.Cqrs.EmptyEnumeratingStreamInvokerAssembly, Version=1.0.0.0");
|
||||
var container = new MicrosoftDiContainer();
|
||||
|
||||
CqrsTestRuntime.RegisterHandlers(container, generatedAssembly.Object);
|
||||
container.Freeze();
|
||||
|
||||
var context = new ArchitectureContext(container);
|
||||
var results = await DrainAsync(context.CreateStream(new GeneratedStreamInvokerRequest(3))).ConfigureAwait(false);
|
||||
Assert.That(results, Is.EqualTo([3, 4]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟返回实例 request invoker 方法的 generated registry。
|
||||
/// </summary>
|
||||
@ -321,15 +357,6 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
ICqrsRequestInvokerProvider,
|
||||
IEnumeratesCqrsRequestInvokerDescriptors
|
||||
{
|
||||
private static readonly CqrsRequestInvokerDescriptorEntry DescriptorEntry = new(
|
||||
typeof(GeneratedRequestInvokerRequest),
|
||||
typeof(string),
|
||||
new CqrsRequestInvokerDescriptor(
|
||||
typeof(IRequestHandler<GeneratedRequestInvokerRequest, string>),
|
||||
typeof(NonStaticRequestInvokerProviderRegistry).GetMethod(
|
||||
nameof(InvokeGenerated),
|
||||
BindingFlags.NonPublic | BindingFlags.Instance)!));
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
@ -347,9 +374,16 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
Type responseType,
|
||||
out CqrsRequestInvokerDescriptor? descriptor)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(requestType);
|
||||
ArgumentNullException.ThrowIfNull(responseType);
|
||||
|
||||
if (requestType == typeof(GeneratedRequestInvokerRequest) && responseType == typeof(string))
|
||||
{
|
||||
descriptor = DescriptorEntry.Descriptor;
|
||||
descriptor = new CqrsRequestInvokerDescriptor(
|
||||
typeof(IRequestHandler<GeneratedRequestInvokerRequest, string>),
|
||||
typeof(NonStaticRequestInvokerProviderRegistry).GetMethod(
|
||||
nameof(InvokeGenerated),
|
||||
BindingFlags.NonPublic | BindingFlags.Instance)!);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -360,7 +394,17 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<CqrsRequestInvokerDescriptorEntry> GetDescriptors()
|
||||
{
|
||||
return [DescriptorEntry];
|
||||
return
|
||||
[
|
||||
new CqrsRequestInvokerDescriptorEntry(
|
||||
typeof(GeneratedRequestInvokerRequest),
|
||||
typeof(string),
|
||||
new CqrsRequestInvokerDescriptor(
|
||||
typeof(IRequestHandler<GeneratedRequestInvokerRequest, string>),
|
||||
typeof(NonStaticRequestInvokerProviderRegistry).GetMethod(
|
||||
nameof(InvokeGenerated),
|
||||
BindingFlags.NonPublic | BindingFlags.Instance)!))
|
||||
];
|
||||
}
|
||||
|
||||
private ValueTask<string> InvokeGenerated(object handler, object request, CancellationToken cancellationToken)
|
||||
@ -403,6 +447,9 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
Type responseType,
|
||||
out CqrsRequestInvokerDescriptor? descriptor)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(requestType);
|
||||
ArgumentNullException.ThrowIfNull(responseType);
|
||||
|
||||
if (requestType == typeof(GeneratedRequestInvokerRequest) && responseType == typeof(string))
|
||||
{
|
||||
descriptor = DescriptorEntry.Descriptor;
|
||||
@ -433,15 +480,6 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
ICqrsStreamInvokerProvider,
|
||||
IEnumeratesCqrsStreamInvokerDescriptors
|
||||
{
|
||||
private static readonly CqrsStreamInvokerDescriptorEntry DescriptorEntry = new(
|
||||
typeof(GeneratedStreamInvokerRequest),
|
||||
typeof(int),
|
||||
new CqrsStreamInvokerDescriptor(
|
||||
typeof(IStreamRequestHandler<GeneratedStreamInvokerRequest, int>),
|
||||
typeof(NonStaticStreamInvokerProviderRegistry).GetMethod(
|
||||
nameof(InvokeGenerated),
|
||||
BindingFlags.NonPublic | BindingFlags.Instance)!));
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
@ -459,9 +497,16 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
Type responseType,
|
||||
out CqrsStreamInvokerDescriptor? descriptor)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(requestType);
|
||||
ArgumentNullException.ThrowIfNull(responseType);
|
||||
|
||||
if (requestType == typeof(GeneratedStreamInvokerRequest) && responseType == typeof(int))
|
||||
{
|
||||
descriptor = DescriptorEntry.Descriptor;
|
||||
descriptor = new CqrsStreamInvokerDescriptor(
|
||||
typeof(IStreamRequestHandler<GeneratedStreamInvokerRequest, int>),
|
||||
typeof(NonStaticStreamInvokerProviderRegistry).GetMethod(
|
||||
nameof(InvokeGenerated),
|
||||
BindingFlags.NonPublic | BindingFlags.Instance)!);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -472,7 +517,17 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<CqrsStreamInvokerDescriptorEntry> GetDescriptors()
|
||||
{
|
||||
return [DescriptorEntry];
|
||||
return
|
||||
[
|
||||
new CqrsStreamInvokerDescriptorEntry(
|
||||
typeof(GeneratedStreamInvokerRequest),
|
||||
typeof(int),
|
||||
new CqrsStreamInvokerDescriptor(
|
||||
typeof(IStreamRequestHandler<GeneratedStreamInvokerRequest, int>),
|
||||
typeof(NonStaticStreamInvokerProviderRegistry).GetMethod(
|
||||
nameof(InvokeGenerated),
|
||||
BindingFlags.NonPublic | BindingFlags.Instance)!))
|
||||
];
|
||||
}
|
||||
|
||||
private object InvokeGenerated(object handler, object request, CancellationToken cancellationToken)
|
||||
@ -515,6 +570,9 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
Type responseType,
|
||||
out CqrsStreamInvokerDescriptor? descriptor)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(requestType);
|
||||
ArgumentNullException.ThrowIfNull(responseType);
|
||||
|
||||
if (requestType == typeof(GeneratedStreamInvokerRequest) && responseType == typeof(int))
|
||||
{
|
||||
descriptor = DescriptorEntry.Descriptor;
|
||||
@ -567,6 +625,9 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
Type responseType,
|
||||
out CqrsRequestInvokerDescriptor? descriptor)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(requestType);
|
||||
ArgumentNullException.ThrowIfNull(responseType);
|
||||
|
||||
if (requestType == typeof(GeneratedRequestInvokerRequest) && responseType == typeof(string))
|
||||
{
|
||||
descriptor = Descriptor;
|
||||
@ -608,6 +669,9 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
Type responseType,
|
||||
out CqrsStreamInvokerDescriptor? descriptor)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(requestType);
|
||||
ArgumentNullException.ThrowIfNull(responseType);
|
||||
|
||||
if (requestType == typeof(GeneratedStreamInvokerRequest) && responseType == typeof(int))
|
||||
{
|
||||
descriptor = Descriptor;
|
||||
@ -619,6 +683,84 @@ internal sealed class CqrsGeneratedRequestInvokerProviderTests
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟实现 request descriptor 枚举契约、但当前不暴露任何 descriptor 的 generated registry。
|
||||
/// </summary>
|
||||
private sealed class EmptyEnumeratingRequestInvokerProviderRegistry :
|
||||
ICqrsHandlerRegistry,
|
||||
ICqrsRequestInvokerProvider,
|
||||
IEnumeratesCqrsRequestInvokerDescriptors
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
services.AddTransient(
|
||||
typeof(IRequestHandler<GeneratedRequestInvokerRequest, string>),
|
||||
typeof(GeneratedRequestInvokerRequestHandler));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetDescriptor(
|
||||
Type requestType,
|
||||
Type responseType,
|
||||
out CqrsRequestInvokerDescriptor? descriptor)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(requestType);
|
||||
ArgumentNullException.ThrowIfNull(responseType);
|
||||
|
||||
descriptor = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<CqrsRequestInvokerDescriptorEntry> GetDescriptors()
|
||||
{
|
||||
return Array.Empty<CqrsRequestInvokerDescriptorEntry>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模拟实现 stream descriptor 枚举契约、但当前不暴露任何 descriptor 的 generated registry。
|
||||
/// </summary>
|
||||
private sealed class EmptyEnumeratingStreamInvokerProviderRegistry :
|
||||
ICqrsHandlerRegistry,
|
||||
ICqrsStreamInvokerProvider,
|
||||
IEnumeratesCqrsStreamInvokerDescriptors
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
ArgumentNullException.ThrowIfNull(logger);
|
||||
|
||||
services.AddTransient(
|
||||
typeof(IStreamRequestHandler<GeneratedStreamInvokerRequest, int>),
|
||||
typeof(GeneratedStreamInvokerRequestHandler));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool TryGetDescriptor(
|
||||
Type requestType,
|
||||
Type responseType,
|
||||
out CqrsStreamInvokerDescriptor? descriptor)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(requestType);
|
||||
ArgumentNullException.ThrowIfNull(responseType);
|
||||
|
||||
descriptor = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<CqrsStreamInvokerDescriptorEntry> GetDescriptors()
|
||||
{
|
||||
return Array.Empty<CqrsStreamInvokerDescriptorEntry>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建带有 generated request invoker registry 元数据的程序集替身。
|
||||
/// </summary>
|
||||
|
||||
@ -32,6 +32,9 @@ internal sealed class HiddenImplementationGeneratedStreamInvokerProviderRegistry
|
||||
/// </summary>
|
||||
/// <param name="services">承载处理器映射的服务集合。</param>
|
||||
/// <param name="logger">用于记录注册诊断的日志器。</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// 当 <paramref name="services" /> 或 <paramref name="logger" /> 为 <see langword="null" /> 时抛出。
|
||||
/// </exception>
|
||||
public void Register(IServiceCollection services, ILogger logger)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(services);
|
||||
@ -50,11 +53,17 @@ internal sealed class HiddenImplementationGeneratedStreamInvokerProviderRegistry
|
||||
/// <param name="responseType">流式响应元素类型。</param>
|
||||
/// <param name="descriptor">命中时返回的描述符。</param>
|
||||
/// <returns>若类型对匹配当前测试流式请求则返回 <see langword="true" />。</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// 当 <paramref name="requestType" /> 或 <paramref name="responseType" /> 为 <see langword="null" /> 时抛出。
|
||||
/// </exception>
|
||||
public bool TryGetDescriptor(
|
||||
Type requestType,
|
||||
Type responseType,
|
||||
out CqrsStreamInvokerDescriptor? descriptor)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(requestType);
|
||||
ArgumentNullException.ThrowIfNull(responseType);
|
||||
|
||||
if (requestType == typeof(HiddenImplementationStreamInvokerContainer.VisibleStreamRequest)
|
||||
&& responseType == typeof(int))
|
||||
{
|
||||
|
||||
@ -19,6 +19,9 @@ public sealed class CqrsRequestInvokerDescriptor(
|
||||
Type handlerType,
|
||||
MethodInfo invokerMethod)
|
||||
{
|
||||
private static readonly string NonStaticInvokerMessage =
|
||||
"CQRS request invoker descriptors require an open static invoker method so generated metadata can be bound deterministically.";
|
||||
|
||||
/// <summary>
|
||||
/// 获取请求处理器在容器中的服务类型。
|
||||
/// </summary>
|
||||
@ -27,5 +30,22 @@ public sealed class CqrsRequestInvokerDescriptor(
|
||||
/// <summary>
|
||||
/// 获取执行请求处理器的开放静态方法。
|
||||
/// </summary>
|
||||
public MethodInfo InvokerMethod { get; } = invokerMethod ?? throw new ArgumentNullException(nameof(invokerMethod));
|
||||
public MethodInfo InvokerMethod { get; } = ValidateInvokerMethod(invokerMethod);
|
||||
|
||||
/// <summary>
|
||||
/// 在描述符构造阶段拒绝实例方法,避免非法 generated metadata 延迟到首次分发时才暴露。
|
||||
/// </summary>
|
||||
/// <param name="invokerMethod">待验证的 generated invoker 方法。</param>
|
||||
/// <returns>通过校验的静态方法。</returns>
|
||||
/// <exception cref="ArgumentNullException">当 <paramref name="invokerMethod" /> 为 <see langword="null" /> 时抛出。</exception>
|
||||
/// <exception cref="ArgumentException">当 <paramref name="invokerMethod" /> 不是静态方法时抛出。</exception>
|
||||
private static MethodInfo ValidateInvokerMethod(MethodInfo invokerMethod)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(invokerMethod);
|
||||
|
||||
if (!invokerMethod.IsStatic)
|
||||
throw new ArgumentException(NonStaticInvokerMessage, nameof(invokerMethod));
|
||||
|
||||
return invokerMethod;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,39 @@ namespace GFramework.Cqrs;
|
||||
/// <summary>
|
||||
/// 描述单个 request/response 类型对与其 generated invoker 元数据之间的映射条目。
|
||||
/// </summary>
|
||||
/// <param name="RequestType">请求运行时类型。</param>
|
||||
/// <param name="ResponseType">响应运行时类型。</param>
|
||||
/// <param name="Descriptor">对应的 generated request invoker 描述符。</param>
|
||||
public sealed record CqrsRequestInvokerDescriptorEntry(
|
||||
Type RequestType,
|
||||
Type ResponseType,
|
||||
CqrsRequestInvokerDescriptor Descriptor);
|
||||
public sealed record CqrsRequestInvokerDescriptorEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化 request invoker 描述符映射条目。
|
||||
/// </summary>
|
||||
/// <param name="requestType">请求运行时类型。</param>
|
||||
/// <param name="responseType">响应运行时类型。</param>
|
||||
/// <param name="descriptor">对应的 generated request invoker 描述符。</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// 当 <paramref name="requestType" />、<paramref name="responseType" /> 或 <paramref name="descriptor" /> 为 <see langword="null" /> 时抛出。
|
||||
/// </exception>
|
||||
public CqrsRequestInvokerDescriptorEntry(
|
||||
Type requestType,
|
||||
Type responseType,
|
||||
CqrsRequestInvokerDescriptor descriptor)
|
||||
{
|
||||
RequestType = requestType ?? throw new ArgumentNullException(nameof(requestType));
|
||||
ResponseType = responseType ?? throw new ArgumentNullException(nameof(responseType));
|
||||
Descriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取请求运行时类型。
|
||||
/// </summary>
|
||||
public Type RequestType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取响应运行时类型。
|
||||
/// </summary>
|
||||
public Type ResponseType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取对应的 generated request invoker 描述符。
|
||||
/// </summary>
|
||||
public CqrsRequestInvokerDescriptor Descriptor { get; }
|
||||
}
|
||||
|
||||
@ -19,6 +19,9 @@ public sealed class CqrsStreamInvokerDescriptor(
|
||||
Type handlerType,
|
||||
MethodInfo invokerMethod)
|
||||
{
|
||||
private static readonly string NonStaticInvokerMessage =
|
||||
"CQRS stream invoker descriptors require an open static invoker method so generated metadata can be bound deterministically.";
|
||||
|
||||
/// <summary>
|
||||
/// 获取流式请求处理器在容器中的服务类型。
|
||||
/// </summary>
|
||||
@ -27,5 +30,22 @@ public sealed class CqrsStreamInvokerDescriptor(
|
||||
/// <summary>
|
||||
/// 获取执行流式请求处理器的开放静态方法。
|
||||
/// </summary>
|
||||
public MethodInfo InvokerMethod { get; } = invokerMethod ?? throw new ArgumentNullException(nameof(invokerMethod));
|
||||
public MethodInfo InvokerMethod { get; } = ValidateInvokerMethod(invokerMethod);
|
||||
|
||||
/// <summary>
|
||||
/// 在描述符构造阶段拒绝实例方法,避免非法 generated metadata 延迟到首次建流时才暴露。
|
||||
/// </summary>
|
||||
/// <param name="invokerMethod">待验证的 generated invoker 方法。</param>
|
||||
/// <returns>通过校验的静态方法。</returns>
|
||||
/// <exception cref="ArgumentNullException">当 <paramref name="invokerMethod" /> 为 <see langword="null" /> 时抛出。</exception>
|
||||
/// <exception cref="ArgumentException">当 <paramref name="invokerMethod" /> 不是静态方法时抛出。</exception>
|
||||
private static MethodInfo ValidateInvokerMethod(MethodInfo invokerMethod)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(invokerMethod);
|
||||
|
||||
if (!invokerMethod.IsStatic)
|
||||
throw new ArgumentException(NonStaticInvokerMessage, nameof(invokerMethod));
|
||||
|
||||
return invokerMethod;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,39 @@ namespace GFramework.Cqrs;
|
||||
/// <summary>
|
||||
/// 描述单个 stream request/response 类型对与其 generated invoker 元数据之间的映射条目。
|
||||
/// </summary>
|
||||
/// <param name="RequestType">流式请求运行时类型。</param>
|
||||
/// <param name="ResponseType">流式响应元素类型。</param>
|
||||
/// <param name="Descriptor">对应的 generated stream invoker 描述符。</param>
|
||||
public sealed record CqrsStreamInvokerDescriptorEntry(
|
||||
Type RequestType,
|
||||
Type ResponseType,
|
||||
CqrsStreamInvokerDescriptor Descriptor);
|
||||
public sealed record CqrsStreamInvokerDescriptorEntry
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化 stream invoker 描述符映射条目。
|
||||
/// </summary>
|
||||
/// <param name="requestType">流式请求运行时类型。</param>
|
||||
/// <param name="responseType">流式响应元素类型。</param>
|
||||
/// <param name="descriptor">对应的 generated stream invoker 描述符。</param>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// 当 <paramref name="requestType" />、<paramref name="responseType" /> 或 <paramref name="descriptor" /> 为 <see langword="null" /> 时抛出。
|
||||
/// </exception>
|
||||
public CqrsStreamInvokerDescriptorEntry(
|
||||
Type requestType,
|
||||
Type responseType,
|
||||
CqrsStreamInvokerDescriptor descriptor)
|
||||
{
|
||||
RequestType = requestType ?? throw new ArgumentNullException(nameof(requestType));
|
||||
ResponseType = responseType ?? throw new ArgumentNullException(nameof(responseType));
|
||||
Descriptor = descriptor ?? throw new ArgumentNullException(nameof(descriptor));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取流式请求运行时类型。
|
||||
/// </summary>
|
||||
public Type RequestType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取流式响应元素类型。
|
||||
/// </summary>
|
||||
public Type ResponseType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取对应的 generated stream invoker 描述符。
|
||||
/// </summary>
|
||||
public CqrsStreamInvokerDescriptor Descriptor { get; }
|
||||
}
|
||||
|
||||
@ -0,0 +1,111 @@
|
||||
# CQRS 重写迁移验证归档(RP-063 至 RP-074)
|
||||
|
||||
## 说明
|
||||
|
||||
- 本文件承接 `cqrs-rewrite-validation-history-through-rp062.md` 之后的详细验证历史。
|
||||
- active tracking 只保留当前权威验证批次、最近 PR 锚点与下一恢复点;更早的命令级明细统一归档到这里。
|
||||
|
||||
## 验证记录
|
||||
|
||||
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --format json --json-output /tmp/current-pr-review.json`
|
||||
- 结果:通过
|
||||
- 备注:确认当时当前分支对应 `PR #305`,并定位到仍需本地复核的 CodeRabbit / Greptile open thread
|
||||
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`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`
|
||||
- 结果:通过
|
||||
- 备注:使用显式 `GIT_DIR` / `GIT_WORK_TREE` 绑定重跑后,`1045` 个 tracked C# 文件的命名校验全部通过;本轮 `_syncRoot` 改名未引入命名规则回归
|
||||
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;本轮确认 notification publisher seam、README 与文档更新未引入 `GFramework.Cqrs` 构建告警
|
||||
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认 stream invoker provider 生成与显式枚举接口实现未引入生成器编译问题
|
||||
- `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认 stream invoker provider fixture 与回归断言可以编译通过
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
|
||||
- 结果:通过
|
||||
- 备注:`4/4` passed;覆盖 generated request / stream invoker provider 的 registrar 接线与 dispatcher 消费语义
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available"`
|
||||
- 结果:通过
|
||||
- 备注:`2/2` passed;确认 generated registry 会同时发射 request / stream invoker provider 描述符与静态 invoker 方法
|
||||
- `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`
|
||||
- 结果:通过
|
||||
- 备注:`1059` 个 tracked C# 文件命名校验全部通过;本轮新增 stream invoker 类型与测试命名未引入回归
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_For_Hidden_Implementation_With_Visible_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_For_Hidden_Implementation_With_Visible_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available"`
|
||||
- 结果:通过
|
||||
- 备注:`4/4` passed;确认 hidden implementation + visible interface 场景也会继续发射 request / stream invoker provider 元数据
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
|
||||
- 结果:通过
|
||||
- 备注:`8/8` passed;补齐 hidden implementation + visible interface 场景后,确认 generated request / stream invoker 在 runtime 侧也会优先命中 provider descriptor
|
||||
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认本轮 precise reflected invoker provider 合同回归未引入 generator 编译告警
|
||||
- `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;并行验证时曾出现过 `MSB3026` 输出文件竞争噪音,随后已串行重跑并得到干净构建结果
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_For_Precise_Reflected_Request_Registrations|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_For_Precise_Reflected_Stream_Registrations|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_For_Hidden_Implementation_With_Visible_Handler_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_For_Hidden_Implementation_With_Visible_Handler_Interface"`
|
||||
- 结果:通过
|
||||
- 备注:`4/4` passed;串行确认 visible-interface hidden-implementation 仍发射 provider 元数据,而 precise reflected 注册继续保持“不发射 provider descriptor”的当前合同
|
||||
- `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:并行执行 build/test 时出现 `MSB3026` 输出文件竞争噪音;无真实编译错误,后续以串行 test 结果作为本轮 authoritative 行为验证
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Provider_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Enumerator|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Provider_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Descriptor_Enumerator|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available"`
|
||||
- 结果:通过
|
||||
- 备注:`6/6` passed;锁定 request / stream provider gate 依赖“provider 接口 + descriptor 枚举接口”同时存在,且原有 happy-path 发射仍保持通过
|
||||
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:并行执行 build/test 时出现 `MSB3026` 输出文件竞争噪音;当前已确认没有新增 analyzer warning,`GFramework.Cqrs.Tests` 仍能完成 Release 构建
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.SendAsync_Should_Throw_When_Generated_Request_Invoker_Is_Not_Static|FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.SendAsync_Should_Throw_When_Generated_Request_Invoker_Is_Incompatible|FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.CreateStream_Should_Throw_When_Generated_Stream_Invoker_Is_Not_Static|FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.CreateStream_Should_Throw_When_Generated_Stream_Invoker_Is_Incompatible|FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.SendAsync_Should_Use_Generated_Request_Invoker_When_Provider_Is_Registered|FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.CreateStream_Should_Use_Generated_Stream_Invoker_When_Provider_Is_Registered"`
|
||||
- 结果:通过
|
||||
- 备注:`6/6` passed;确认 request / stream 的非法 generated invoker 现统一抛出 `InvalidOperationException`,且原有 happy-path 未回归
|
||||
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认新增 non-enumerating provider 回归未引入构建告警
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
|
||||
- 结果:通过
|
||||
- 备注:`14/14` passed;确认 request / stream 的 generated happy-path、异常路径与 non-enumerating provider 反射回退语义均保持通过
|
||||
- `dotnet build GFramework.Core/GFramework.Core.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认 `CqrsRuntimeModule` 接线变更未引入 `GFramework.Core` 模块构建问题
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsNotificationPublisherTests"`
|
||||
- 结果:通过
|
||||
- 备注:`5/5` 通过;覆盖自定义 publisher 顺序、上下文注入、零处理器、首错即停与默认接线复用
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests"`
|
||||
- 结果:通过
|
||||
- 备注:`41/41` 通过;确认 CQRS 基础设施默认接线与容器行为未回归
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests"`
|
||||
- 结果:通过
|
||||
- 备注:`42/42` 通过;本轮新增 legacy alias 回填回归后,确认正式 seam 与旧命名空间 alias 仍指向同一实例
|
||||
- `dotnet build GFramework.Core/GFramework.Core.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认 legacy alias helper 收敛与文档更新未引入 `GFramework.Core` 模块构建告警
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests|FullyQualifiedName~CqrsHandlerRegistrarTests|FullyQualifiedName~CqrsDispatcherCacheTests"`
|
||||
- 结果:通过
|
||||
- 备注:`22/22` 通过;确认 generated request invoker provider 的 registrar 接线、dispatcher 消费与现有 request/notification/stream cache 语义未回归
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available"`
|
||||
- 结果:通过
|
||||
- 备注:`1/1` 通过;锁定 generator 会在 runtime 合同可用时发射 request invoker provider 成员
|
||||
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认 request invoker provider seam 与 dispatcher/registrar 接线未引入新增构建告警
|
||||
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认三份 `Mediator` 命名收口后的 CQRS 测试项目构建仍然干净
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~ArchitectureContextTests"`
|
||||
- 结果:通过
|
||||
- 备注:`22/22` 通过;新增 `PublishAsync` / `CreateStream` 并发首次访问只解析一次 `ICqrsRuntime` 的回归
|
||||
@ -7,7 +7,7 @@ CQRS 迁移与收敛。
|
||||
|
||||
## 当前恢复点
|
||||
|
||||
- 恢复点编号:`CQRS-REWRITE-RP-074`
|
||||
- 恢复点编号:`CQRS-REWRITE-RP-075`
|
||||
- 当前阶段:`Phase 8`
|
||||
- 当前焦点:
|
||||
- 已完成一轮 `CQRS vs Mediator` 只读评估归档,结论已沉淀到 `archive/todos/cqrs-vs-mediator-assessment-rp063.md`
|
||||
@ -81,6 +81,12 @@ CQRS 迁移与收敛。
|
||||
- 已完成一轮 non-enumerating provider reflection fallback 回归:
|
||||
- `GFramework.Cqrs.Tests/Cqrs/CqrsGeneratedRequestInvokerProviderTests.cs` 现新增 request / stream 两条回归,锁定当 registry 只暴露 provider 接口、但不实现 `IEnumeratesCqrs*InvokerDescriptors` 时,registrar 不会预热 dispatcher 缓存,后续 dispatch 会继续回退到既有反射路径
|
||||
- 当前回归明确区分“provider 已注册”和“descriptor 已枚举入缓存”这两个阶段,避免后续把 `TryGetDescriptor(...)` 的存在误当成 dispatcher 会主动查询 provider 的合同
|
||||
- 已完成 `PR #307` review follow-up 的当前批次:
|
||||
- `CqrsRequestInvokerDescriptor` / `CqrsStreamInvokerDescriptor` 现会在构造阶段拒绝实例方法,避免非法 generated metadata 延迟到首次分发时才暴露
|
||||
- `CqrsRequestInvokerDescriptorEntry` / `CqrsStreamInvokerDescriptorEntry` 现补齐公开入口空值防御,保持 request / stream 描述符合同一致
|
||||
- `CqrsGeneratedRequestInvokerProviderTests` 现补齐空 descriptor 枚举回退回归,并把“非静态 invoker”语义收敛为 registrar 放弃 generated registry 后回退到反射路径
|
||||
- `docs/zh-CN/core/cqrs.md` 已改正文档里“元数据异常会回退到反射”的错误表述,并将源码阅读表中的 `Internal/` 路径文案改为语义标签
|
||||
- active tracking 已把 `RP-063` 至 `RP-074` 的命令级验证明细迁移到 `archive/todos/cqrs-rewrite-validation-history-rp063-through-rp074.md`,当前入口只保留最近权威验证与恢复点
|
||||
- 当前相对 `origin/main` 的累计 branch diff 为 `24 files / 1754 changed lines`,仍低于本轮 `$gframework-batch-boot 50` 的主要 stop condition,可继续推进下一批低风险切片
|
||||
- 已将 mixed fallback 场景进一步收敛:当 runtime 允许同一程序集声明多个 `CqrsReflectionFallbackAttribute` 实例时,generator 现会把可直接引用的 fallback handlers 与仅能按名称恢复的 fallback handlers 拆分发射
|
||||
- `CqrsReflectionFallbackAttribute` 现允许多实例,以承载 `Type[]` 与字符串 fallback 元数据的组合输出
|
||||
@ -236,6 +242,7 @@ CQRS 迁移与收敛。
|
||||
|
||||
- 历史跟踪归档:[cqrs-rewrite-history-through-rp043.md](../archive/todos/cqrs-rewrite-history-through-rp043.md)
|
||||
- 验证历史归档:[cqrs-rewrite-validation-history-through-rp062.md](../archive/todos/cqrs-rewrite-validation-history-through-rp062.md)
|
||||
- `RP-063` 至 `RP-074` 验证归档:[cqrs-rewrite-validation-history-rp063-through-rp074.md](../archive/todos/cqrs-rewrite-validation-history-rp063-through-rp074.md)
|
||||
- CQRS 与 Mediator 评估归档:[cqrs-vs-mediator-assessment-rp063.md](../archive/todos/cqrs-vs-mediator-assessment-rp063.md)
|
||||
- 历史 trace 归档:[cqrs-rewrite-history-through-rp043.md](../archive/traces/cqrs-rewrite-history-through-rp043.md)
|
||||
- `RP-046` 至 `RP-061` trace 归档:[cqrs-rewrite-history-rp046-through-rp061.md](../archive/traces/cqrs-rewrite-history-rp046-through-rp061.md)
|
||||
@ -244,111 +251,19 @@ CQRS 迁移与收敛。
|
||||
|
||||
- `RP-043` 之前的详细阶段记录、定向验证命令和阶段性决策均已移入主题内归档
|
||||
- `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`
|
||||
- `RP-063` 至 `RP-074` 的详细验证命令与阶段性结果已移入验证归档,active tracking 只保留当前 PR 复核批次的权威结果
|
||||
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/current-pr-review.json`
|
||||
- 结果:通过
|
||||
- 备注:确认当前分支对应 `PR #305`,并定位到仍需本地复核的 CodeRabbit / Greptile open thread
|
||||
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`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 生成分支注释与断言顺序修正未改变生成语义
|
||||
- 备注:确认当前分支对应 `PR #307`,状态为 `OPEN`;当前仍有 `9` 条 CodeRabbit open thread,本轮只接受其中经本地复核后仍成立的合同防御、文档和 tracking 建议
|
||||
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:构建成功;并行验证期间出现过 `MSB3026` 拷贝重试噪音,属于同时运行多个 `dotnet` 命令时的输出文件竞争,不是持久性编译 warning
|
||||
- `bash scripts/validate-csharp-naming.sh`
|
||||
- 结果:通过
|
||||
- 备注:使用显式 `GIT_DIR` / `GIT_WORK_TREE` 绑定重跑后,`1045` 个 tracked C# 文件的命名校验全部通过;本轮 `_syncRoot` 改名未引入命名规则回归
|
||||
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;本轮确认 notification publisher seam、README 与文档更新未引入 `GFramework.Cqrs` 构建告警
|
||||
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认 stream invoker provider 生成与显式枚举接口实现未引入生成器编译问题
|
||||
- `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认 stream invoker provider fixture 与回归断言可以编译通过
|
||||
- 备注:`0 warning / 0 error`;确认描述符前置防御、XML 文档与文档修正未引入 `GFramework.Cqrs` 模块告警
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
|
||||
- 结果:通过
|
||||
- 备注:`4/4` passed;覆盖 generated request / stream invoker provider 的 registrar 接线与 dispatcher 消费语义
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available"`
|
||||
- 结果:通过
|
||||
- 备注:`2/2` passed;确认 generated registry 会同时发射 request / stream invoker provider 描述符与静态 invoker 方法
|
||||
- `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`
|
||||
- 结果:通过
|
||||
- 备注:`1059` 个 tracked C# 文件命名校验全部通过;本轮新增 stream invoker 类型与测试命名未引入回归
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_For_Hidden_Implementation_With_Visible_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_For_Hidden_Implementation_With_Visible_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available"`
|
||||
- 结果:通过
|
||||
- 备注:`4/4` passed;确认 hidden implementation + visible interface 场景也会继续发射 request / stream invoker provider 元数据
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
|
||||
- 结果:通过
|
||||
- 备注:`8/8` passed;补齐 hidden implementation + visible interface 场景后,确认 generated request / stream invoker 在 runtime 侧也会优先命中 provider descriptor
|
||||
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认本轮 precise reflected invoker provider 合同回归未引入 generator 编译告警
|
||||
- `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;并行验证时曾出现过 `MSB3026` 输出文件竞争噪音,随后已串行重跑并得到干净构建结果
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_For_Precise_Reflected_Request_Registrations|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_For_Precise_Reflected_Stream_Registrations|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_For_Hidden_Implementation_With_Visible_Handler_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_For_Hidden_Implementation_With_Visible_Handler_Interface"`
|
||||
- 结果:通过
|
||||
- 备注:`4/4` passed;串行确认 visible-interface hidden-implementation 仍发射 provider 元数据,而 precise reflected 注册继续保持“不发射 provider descriptor”的当前合同
|
||||
- `dotnet build GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:并行执行 build/test 时出现 `MSB3026` 输出文件竞争噪音;无真实编译错误,后续以串行 test 结果作为本轮 authoritative 行为验证
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Provider_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Request_Invoker_Provider_Metadata_When_Runtime_Lacks_Request_Descriptor_Enumerator|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Provider_Interface|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Does_Not_Emit_Stream_Invoker_Provider_Metadata_When_Runtime_Lacks_Stream_Descriptor_Enumerator|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available|FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Stream_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available"`
|
||||
- 结果:通过
|
||||
- 备注:`6/6` passed;锁定 request / stream provider gate 依赖“provider 接口 + descriptor 枚举接口”同时存在,且原有 happy-path 发射仍保持通过
|
||||
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:并行执行 build/test 时出现 `MSB3026` 输出文件竞争噪音;当前已确认没有新增 analyzer warning,`GFramework.Cqrs.Tests` 仍能完成 Release 构建
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.SendAsync_Should_Throw_When_Generated_Request_Invoker_Is_Not_Static|FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.SendAsync_Should_Throw_When_Generated_Request_Invoker_Is_Incompatible|FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.CreateStream_Should_Throw_When_Generated_Stream_Invoker_Is_Not_Static|FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.CreateStream_Should_Throw_When_Generated_Stream_Invoker_Is_Incompatible|FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.SendAsync_Should_Use_Generated_Request_Invoker_When_Provider_Is_Registered|FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests.CreateStream_Should_Use_Generated_Stream_Invoker_When_Provider_Is_Registered"`
|
||||
- 结果:通过
|
||||
- 备注:`6/6` passed;确认 request / stream 的非法 generated invoker 现统一抛出 `InvalidOperationException`,且原有 happy-path 未回归
|
||||
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认新增 non-enumerating provider 回归未引入构建告警
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
|
||||
- 结果:通过
|
||||
- 备注:`14/14` passed;确认 request / stream 的 generated happy-path、异常路径与 non-enumerating provider 反射回退语义均保持通过
|
||||
- `dotnet build GFramework.Core/GFramework.Core.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认 `CqrsRuntimeModule` 接线变更未引入 `GFramework.Core` 模块构建问题
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsNotificationPublisherTests"`
|
||||
- 结果:通过
|
||||
- 备注:`5/5` 通过;覆盖自定义 publisher 顺序、上下文注入、零处理器、首错即停与默认接线复用
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests"`
|
||||
- 结果:通过
|
||||
- 备注:`41/41` 通过;确认 CQRS 基础设施默认接线与容器行为未回归
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~MicrosoftDiContainerTests"`
|
||||
- 结果:通过
|
||||
- 备注:`42/42` 通过;本轮新增 legacy alias 回填回归后,确认正式 seam 与旧命名空间 alias 仍指向同一实例
|
||||
- `dotnet build GFramework.Core/GFramework.Core.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认 legacy alias helper 收敛与文档更新未引入 `GFramework.Core` 模块构建告警
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests|FullyQualifiedName~CqrsHandlerRegistrarTests|FullyQualifiedName~CqrsDispatcherCacheTests"`
|
||||
- 结果:通过
|
||||
- 备注:`22/22` 通过;确认 generated request invoker provider 的 registrar 接线、dispatcher 消费与现有 request/notification/stream cache 语义未回归
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests.Emits_Request_Invoker_Provider_Metadata_When_Runtime_Contract_Is_Available"`
|
||||
- 结果:通过
|
||||
- 备注:`1/1` 通过;锁定 generator 会在 runtime 合同可用时发射 request invoker provider 成员
|
||||
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认 request invoker provider seam 与 dispatcher/registrar 接线未引入新增构建告警
|
||||
- `dotnet build GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`;确认三份 `Mediator` 命名收口后的 CQRS 测试项目构建仍然干净
|
||||
- `dotnet test GFramework.Core.Tests/GFramework.Core.Tests.csproj -c Release --filter "FullyQualifiedName~ArchitectureContextTests"`
|
||||
- 结果:通过
|
||||
- 备注:`22/22` 通过;新增 `PublishAsync` / `CreateStream` 并发首次访问只解析一次 `ICqrsRuntime` 的回归
|
||||
- 备注:`16/16` passed;确认非静态 invoker 回退语义、空 descriptor 枚举回退,以及既有 generated / incompatible 分支均保持通过
|
||||
|
||||
## 下一步
|
||||
|
||||
1. 在保持 branch diff 明显低于 `50 files` 的前提下,继续挑选下一批低风险 `dispatch/invoker` 收敛切片,并优先考虑 request / stream provider 的剩余 runtime 失败边界、缓存预热边界或 generator gate 合同补强
|
||||
1. 在保持 branch diff 明显低于 `50 files` 的前提下,继续挑选下一批低风险 `dispatch/invoker` 收敛切片,并优先考虑 request / stream provider 的剩余缓存预热边界或 generator gate 合同补强
|
||||
2. 基于已落地的 notification publisher seam,评估是否需要第二阶段公开配置面、并行 publisher 或 telemetry decorator
|
||||
3. 单独规划旧 `Command` / `Query` API 的收口顺序;`LegacyICqrsRuntime` compatibility slice 已收口到显式 helper 与专门测试,可暂时移出最高优先级
|
||||
|
||||
@ -2,6 +2,35 @@
|
||||
|
||||
## 2026-04-30
|
||||
|
||||
### 阶段:PR #307 review follow-up 收敛(CQRS-REWRITE-RP-075)
|
||||
|
||||
- 在 `RP-074` 后继续沿用 `gframework-batch-boot 50` 的低风险切片策略,本轮只处理 `$gframework-pr-review` 对当前 `PR #307` 仍然成立的本地问题
|
||||
- 主线程先用 `fetch_current_pr_review.py --json-output /tmp/current-pr-review.json` 抓取 PR #307 的 latest-head open threads,确认真正仍需处理的项集中在:
|
||||
- stream/request invoker 描述符入口缺少更早的合同防御
|
||||
- request/stream provider 测试缺少“实现枚举契约但返回空 descriptor 集合”的回退覆盖
|
||||
- `docs/zh-CN/core/cqrs.md` 把 generated metadata 不兼容时的行为误写成“回退到反射”
|
||||
- active tracking 累积了 `RP-063` 至 `RP-074` 的长验证历史,不再适合作为默认恢复入口
|
||||
- 本轮实现收敛:
|
||||
- `GFramework.Cqrs/CqrsRequestInvokerDescriptor.cs` 与 `GFramework.Cqrs/CqrsStreamInvokerDescriptor.cs` 现会在构造阶段拒绝实例方法,把非法 generated metadata 失败点前移到 registrar 激活/预热阶段
|
||||
- `GFramework.Cqrs/CqrsRequestInvokerDescriptorEntry.cs` 与 `GFramework.Cqrs/CqrsStreamInvokerDescriptorEntry.cs` 现补齐公开构造入口的空值防御,并保持 request / stream 形状对称
|
||||
- `GFramework.Cqrs.Tests/Cqrs/CqrsGeneratedRequestInvokerProviderTests.cs` 现补齐 request / stream 的空 descriptor 枚举回退回归,并把“非静态 invoker”断言从首次分发抛错收敛为 registrar 放弃 generated registry 后回退到反射路径
|
||||
- `GFramework.Cqrs.Tests/Cqrs/HiddenImplementationGeneratedStreamInvokerProviderRegistry.cs` 现补齐 `<exception>` XML 注释与 `TryGetDescriptor(...)` 参数空值防御
|
||||
- `docs/zh-CN/core/cqrs.md` 现明确区分“未命中 generated descriptor 时回退到反射绑定”和“已命中的不兼容 generated metadata 会直接抛错”,并把 reader-facing 表格里的 `Internal/` 路径标签改成语义文案
|
||||
- `ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md` 现把恢复点推进到 `RP-075`,同时把 `RP-063` 至 `RP-074` 的命令级验证历史迁移到新的归档文件,active 入口只保留最近 PR 锚点与权威验证
|
||||
|
||||
### 验证(RP-075)
|
||||
|
||||
- `dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release`
|
||||
- 结果:通过,`0 warning / 0 error`
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsGeneratedRequestInvokerProviderTests"`
|
||||
- 结果:通过,`16/16` passed
|
||||
|
||||
### 当前下一步(RP-075)
|
||||
|
||||
1. 提交本轮 PR #307 review follow-up 收敛,保持恢复点、trace 与已验证代码状态一致
|
||||
2. 若继续下一批,优先挑选 request / stream provider 的缓存预热边界或 generator gate 合同补强,而不是扩散到新的模块
|
||||
3. 保持只暂存本轮相关文件,避免把工作区里无关的 `.gitignore` 本地改动混入提交
|
||||
|
||||
### 阶段:non-enumerating provider reflection fallback 回归(CQRS-REWRITE-RP-074)
|
||||
|
||||
- 在 `RP-073` 提交后继续按 `gframework-batch-boot 50` 执行;当前 branch diff 相对 `origin/main` 仍远低于 `50 files` 阈值,因此继续追加一轮单文件 runtime contract 回归
|
||||
|
||||
@ -167,7 +167,7 @@ protected override void OnInitialize()
|
||||
2. 存在生成注册器时优先使用 `ICqrsHandlerRegistry`
|
||||
3. 当生成注册器同时暴露 generated request invoker provider 时,runtime 会把 request/response 类型对对应的 descriptor 预先接线到 dispatcher 缓存,后续请求分发优先消费这些 generated request invoker 元数据
|
||||
4. 当生成注册器同时暴露 generated stream invoker provider 时,runtime 会以同样方式优先消费 stream request 对应的 generated stream invoker descriptor;只有当前类型对未命中时,才回退到既有反射 stream binding
|
||||
5. 生成注册器不可用或元数据异常时记录告警并回退到反射路径
|
||||
5. 生成注册器不可用时记录告警并回退到反射路径;只有“未命中 generated descriptor”才会走反射绑定,已命中的不兼容元数据会直接抛出异常
|
||||
6. 当生成注册器携带 `CqrsReflectionFallbackAttribute` 元数据时,运行时会先完成生成注册器注册,再补剩余 handler
|
||||
7. `CqrsReflectionFallbackAttribute` 可以同时携带 `Type[]` 和 `string[]` 两类清单;运行时会优先复用直接 `Type` 条目,只对名称条目做定向 `Assembly.GetType(...)` 查找
|
||||
8. 只有旧版空 marker 或生成注册器不可用时,才会回到整程序集反射扫描
|
||||
@ -236,7 +236,7 @@ RegisterCqrsPipelineBehavior<LoggingBehavior<,>>();
|
||||
| `GFramework.Cqrs.Abstractions/Cqrs/` | `ICqrsRuntime`、`ICqrsHandlerRegistrar`、`IPipelineBehavior<,>`、`IRequestHandler<,>`、`Unit` | 请求、处理器和 runtime seam 的最小契约 |
|
||||
| `GFramework.Cqrs/Command` `Query` `Notification` `Request` `Extensions` | `CommandBase<TInput, TResponse>`、`QueryBase<TInput, TResponse>`、`NotificationBase<TInput>`、`RequestBase<TInput, TResponse>`、`ContextAwareCqrsExtensions` | 业务侧常用基类和上下文发送入口 |
|
||||
| `GFramework.Cqrs/Cqrs/` | `AbstractCommandHandler<,>`、`AbstractQueryHandler<,>`、`AbstractRequestHandler<,>`、`AbstractStreamCommandHandler<,>`、`AbstractStreamQueryHandler<,>`、`LoggingBehavior<,>` | 默认处理器基类、上下文注入、流式处理与行为管道 |
|
||||
| `GFramework.Cqrs` 根入口与 `Internal/` | `CqrsRuntimeFactory`、`ICqrsHandlerRegistry`、`CqrsHandlerRegistryAttribute`、`CqrsReflectionFallbackAttribute`、`ICqrsRequestInvokerProvider`、`ICqrsStreamInvokerProvider` | runtime 创建入口、generated-registry 优先级、request / stream invoker provider 协作点、targeted fallback 语义和程序集去重规则 |
|
||||
| 运行时入口与内部协作层 | `CqrsRuntimeFactory`、`ICqrsHandlerRegistry`、`CqrsHandlerRegistryAttribute`、`CqrsReflectionFallbackAttribute`、`ICqrsRequestInvokerProvider`、`ICqrsStreamInvokerProvider` | runtime 创建入口、generated-registry 优先级、request / stream invoker provider 协作点、targeted fallback 语义和程序集去重规则 |
|
||||
| `GFramework.Cqrs.SourceGenerators/Cqrs/` | `CqrsHandlerRegistryGenerator`、`RuntimeTypeReferenceSpec`、`OrderedRegistrationKind` | 生成注册器、可直接引用类型判定、mixed fallback 发射与诊断边界 |
|
||||
|
||||
## 继续阅读
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user