mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-06 16:16:44 +08:00
fix(cqrs): 收敛 fallback 审查跟进
- 修复 generator preamble 的多实例 fallback 特性排版并移除死参数 - 补强 mixed/direct fallback 生成回归断言并拒绝空 marker - 更新 CQRS 审查跟踪记录与 XML 文档
This commit is contained in:
parent
76fcdb8233
commit
8d8b94f608
@ -8,15 +8,12 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
||||
/// <summary>
|
||||
/// 生成程序集级 CQRS handler 注册器源码。
|
||||
/// </summary>
|
||||
/// <param name="generationEnvironment">
|
||||
/// 当前轮次的生成环境,用于决定 runtime 是否提供 <c>CqrsReflectionFallbackAttribute</c> 契约,以及是否需要在输出中发射对应的程序集级元数据。
|
||||
/// </param>
|
||||
/// <param name="registrations">
|
||||
/// 已整理并排序的 handler 注册描述。方法会据此生成 <c>CqrsHandlerRegistry.g.cs</c>,其中包含直接注册、实现类型反射注册、精确运行时类型查找等分支。
|
||||
/// </param>
|
||||
/// <param name="reflectionFallbackEmission">
|
||||
/// 当前轮次选定的程序集级 reflection fallback 元数据发射策略。
|
||||
/// 调用方必须先确保:若该策略包含 fallback handlers,则 <paramref name="generationEnvironment" /> 已声明支持对应的 fallback attribute 契约;
|
||||
/// 调用方必须先确保:若该策略包含 fallback handlers,则当前 runtime 已声明支持对应的 fallback attribute 契约;
|
||||
/// 否则应在进入本方法前报告诊断并放弃生成,而不是输出会静默漏注册的半成品注册器。
|
||||
/// </param>
|
||||
/// <returns>完整的注册器源代码文本。</returns>
|
||||
@ -28,13 +25,12 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
||||
/// 该方法本身不报告诊断;“fallback 必需但 runtime 契约缺失”的错误由调用方在进入本方法前处理。
|
||||
/// </remarks>
|
||||
private static string GenerateSource(
|
||||
GenerationEnvironment generationEnvironment,
|
||||
IReadOnlyList<ImplementationRegistrationSpec> registrations,
|
||||
ReflectionFallbackEmissionSpec reflectionFallbackEmission)
|
||||
{
|
||||
var sourceShape = CreateGeneratedRegistrySourceShape(registrations);
|
||||
var builder = new StringBuilder();
|
||||
AppendGeneratedSourcePreamble(builder, generationEnvironment, reflectionFallbackEmission);
|
||||
AppendGeneratedSourcePreamble(builder, reflectionFallbackEmission);
|
||||
AppendGeneratedRegistryType(builder, registrations, sourceShape);
|
||||
return builder.ToString();
|
||||
}
|
||||
@ -68,11 +64,9 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
||||
/// 发射生成文件头、nullable 指令以及注册器所需的程序集级元数据特性。
|
||||
/// </summary>
|
||||
/// <param name="builder">生成源码构造器。</param>
|
||||
/// <param name="generationEnvironment">当前轮次的生成环境。</param>
|
||||
/// <param name="reflectionFallbackEmission">需要写入程序集级 reflection fallback 特性的元数据策略。</param>
|
||||
private static void AppendGeneratedSourcePreamble(
|
||||
StringBuilder builder,
|
||||
GenerationEnvironment generationEnvironment,
|
||||
ReflectionFallbackEmissionSpec reflectionFallbackEmission)
|
||||
{
|
||||
builder.AppendLine("// <auto-generated />");
|
||||
@ -102,10 +96,14 @@ public sealed partial class CqrsHandlerRegistryGenerator
|
||||
StringBuilder builder,
|
||||
ReflectionFallbackEmissionSpec reflectionFallbackEmission)
|
||||
{
|
||||
foreach (var attributeEmission in reflectionFallbackEmission.Attributes)
|
||||
for (var index = 0; index < reflectionFallbackEmission.Attributes.Length; index++)
|
||||
{
|
||||
AppendReflectionFallbackAttribute(builder, attributeEmission);
|
||||
builder.AppendLine();
|
||||
if (index > 0)
|
||||
{
|
||||
builder.AppendLine();
|
||||
}
|
||||
|
||||
AppendReflectionFallbackAttribute(builder, reflectionFallbackEmission.Attributes[index]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -296,7 +296,7 @@ public sealed partial class CqrsHandlerRegistryGenerator : IIncrementalGenerator
|
||||
|
||||
context.AddSource(
|
||||
HintName,
|
||||
GenerateSource(generationEnvironment, registrations, reflectionFallbackEmission));
|
||||
GenerateSource(registrations, reflectionFallbackEmission));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -13,6 +13,9 @@ internal sealed class ReflectionFallbackNotificationContainer
|
||||
/// <summary>
|
||||
/// 获取可被直接引用、适合通过 <see cref="Type" /> 元数据补扫的处理器类型。
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// 可被生成注册器直接引用的 fallback 处理器类型,用于验证 runtime 会优先消费 <see cref="Type" /> 元数据。
|
||||
/// </returns>
|
||||
public static Type DirectFallbackHandlerType => typeof(DirectFallbackGeneratedRegistryNotificationHandler);
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -1876,6 +1876,7 @@ public class CqrsHandlerRegistryGeneratorTests
|
||||
var generatorErrors = execution.GeneratorDiagnostics
|
||||
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
||||
.ToArray();
|
||||
var generatedSource = execution.GeneratedSources[0].content;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@ -1885,13 +1886,24 @@ public class CqrsHandlerRegistryGeneratorTests
|
||||
Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
|
||||
Assert.That(execution.GeneratedSources[0].filename, Is.EqualTo("CqrsHandlerRegistry.g.cs"));
|
||||
Assert.That(
|
||||
execution.GeneratedSources[0].content,
|
||||
generatedSource,
|
||||
Does.Contain(
|
||||
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute(typeof(global::TestApp.Container.AlphaHandler), typeof(global::TestApp.Container.BetaHandler))]"));
|
||||
Assert.That(
|
||||
execution.GeneratedSources[0].content,
|
||||
generatedSource,
|
||||
Does.Not.Contain(
|
||||
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute(\"TestApp.Container+AlphaHandler\", \"TestApp.Container+BetaHandler\")]"));
|
||||
Assert.That(generatedSource, Does.Not.Contain("CqrsReflectionFallbackAttribute()"));
|
||||
Assert.That(
|
||||
CountOccurrences(
|
||||
generatedSource,
|
||||
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute"),
|
||||
Is.EqualTo(1));
|
||||
Assert.That(
|
||||
CountOccurrences(
|
||||
generatedSource,
|
||||
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute(\""),
|
||||
Is.Zero);
|
||||
});
|
||||
}
|
||||
|
||||
@ -1915,6 +1927,7 @@ public class CqrsHandlerRegistryGeneratorTests
|
||||
var generatorErrors = execution.GeneratorDiagnostics
|
||||
.Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
|
||||
.ToArray();
|
||||
var generatedSource = execution.GeneratedSources[0].content;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@ -1924,13 +1937,32 @@ public class CqrsHandlerRegistryGeneratorTests
|
||||
Assert.That(execution.GeneratedSources, Has.Length.EqualTo(1));
|
||||
Assert.That(execution.GeneratedSources[0].filename, Is.EqualTo("CqrsHandlerRegistry.g.cs"));
|
||||
Assert.That(
|
||||
execution.GeneratedSources[0].content,
|
||||
generatedSource,
|
||||
Does.Contain(
|
||||
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute(typeof(global::TestApp.Container.AlphaHandler))]"));
|
||||
Assert.That(
|
||||
execution.GeneratedSources[0].content,
|
||||
generatedSource,
|
||||
Does.Contain(
|
||||
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute(\"TestApp.Container+BetaHandler\")]"));
|
||||
Assert.That(generatedSource, Does.Not.Contain("CqrsReflectionFallbackAttribute()"));
|
||||
Assert.That(
|
||||
CountOccurrences(
|
||||
generatedSource,
|
||||
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute"),
|
||||
Is.EqualTo(2));
|
||||
Assert.That(
|
||||
CountOccurrences(
|
||||
generatedSource,
|
||||
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute(\""),
|
||||
Is.EqualTo(1));
|
||||
Assert.That(
|
||||
generatedSource,
|
||||
Does.Contain(
|
||||
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute(typeof(global::TestApp.Container.AlphaHandler))]" +
|
||||
Environment.NewLine +
|
||||
"[assembly: global::GFramework.Cqrs.CqrsReflectionFallbackAttribute(\"TestApp.Container+BetaHandler\")]" +
|
||||
Environment.NewLine +
|
||||
"[assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]"));
|
||||
});
|
||||
}
|
||||
|
||||
@ -1985,6 +2017,31 @@ public class CqrsHandlerRegistryGeneratorTests
|
||||
return execution.GeneratedSources[0].content;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 统计生成源码中某个固定片段的出现次数,用于锁定程序集级 fallback 特性的发射个数。
|
||||
/// </summary>
|
||||
/// <param name="text">待统计的完整生成源码。</param>
|
||||
/// <param name="value">需要计数的固定片段。</param>
|
||||
/// <returns><paramref name="value" /> 在 <paramref name="text" /> 中出现的次数。</returns>
|
||||
private static int CountOccurrences(string text, string value)
|
||||
{
|
||||
if (string.IsNullOrEmpty(value))
|
||||
throw new ArgumentException("The search value must not be null or empty.", nameof(value));
|
||||
|
||||
var count = 0;
|
||||
var startIndex = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
var nextIndex = text.IndexOf(value, startIndex, global::System.StringComparison.Ordinal);
|
||||
if (nextIndex < 0)
|
||||
return count;
|
||||
|
||||
count++;
|
||||
startIndex = nextIndex + value.Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行 CQRS handler registry generator,并返回生成输出及相关诊断。
|
||||
/// </summary>
|
||||
|
||||
@ -66,6 +66,11 @@ CQRS 迁移与收敛。
|
||||
- 当本轮 fallback 同时包含可直接引用与仅能按名称恢复的 handlers,且 runtime 同时支持 `Type[]`、`string[]` 和多实例特性时,生成器会拆分输出两段 fallback 元数据
|
||||
- `GFramework.Cqrs.Tests` 已补充 mixed fallback metadata 回归,锁定 registrar 只对字符串条目执行定向 `Assembly.GetType(...)`
|
||||
- `GFramework.SourceGenerators.Tests` 已补充 mixed fallback emission 回归,锁定 generator 会输出两个程序集级 fallback 特性实例而不是整体退回字符串
|
||||
- `2026-04-29` 已重新执行 `$gframework-pr-review`:
|
||||
- 当前分支对应 `PR #302`,状态为 `OPEN`
|
||||
- latest reviewed commit 当前剩余 `3` 条 open AI review threads:`2` 条 Greptile、`1` 条 CodeRabbit
|
||||
- 本地核对后确认 `dotnet-format` 仍只有 `Restore operation failed` 噪音,没有附带当前仍成立的文件级格式诊断
|
||||
- 已按 review triage 修正 generator source preamble 的多实例 fallback 特性排版、移除死参数,并补强 mixed/direct fallback 发射回归断言与 XML 文档
|
||||
- 当前主线优先级:
|
||||
- generator 覆盖面继续扩大
|
||||
- dispatch/invoker 反射占比继续下降
|
||||
@ -116,6 +121,18 @@ CQRS 迁移与收敛。
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
|
||||
- 结果:通过
|
||||
- 备注:`18/18` 测试通过;本轮覆盖 mixed fallback metadata 的双特性发射路径
|
||||
- `python3 .agents/skills/gframework-pr-review/scripts/fetch_current_pr_review.py --json-output /tmp/gframework-pr-review.json`
|
||||
- 结果:通过
|
||||
- 备注:确认当前分支对应 `PR #302`;latest head review 仍有 `3` 条 open AI threads,其中 MegaLinter 仅报告 `dotnet-format` restore failure 噪音
|
||||
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
|
||||
- 结果:通过
|
||||
- 备注:`0 warning / 0 error`
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
|
||||
- 结果:通过
|
||||
- 备注:`18/18` 测试通过;本轮直接覆盖 fallback preamble 排版与特性个数断言收紧
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
|
||||
- 结果:通过
|
||||
- 备注:`13/13` 测试通过;本轮确认 mixed fallback metadata 的 registrar 消费路径未回归
|
||||
|
||||
## 下一步
|
||||
|
||||
|
||||
@ -32,6 +32,22 @@
|
||||
- `13/13` passed
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
|
||||
- `18/18` passed
|
||||
- 随后按 `$gframework-pr-review` 重新拉取当前分支 PR 审查数据:
|
||||
- 当前 worktree `feat/cqrs-optimization` 已对应 `PR #302`
|
||||
- latest head commit 仍有 `3` 条 open AI review threads:Greptile 指向 generator preamble 的死参数与多实例 fallback 特性空行,CodeRabbit 指向 mixed/direct fallback 测试断言过宽
|
||||
- MegaLinter 仍只暴露 `dotnet-format` 的 `Restore operation failed`,未给出本地仍成立的格式文件线索,因此按环境噪音处理
|
||||
- 本轮已继续收口 `RP-052` 的 follow-up:
|
||||
- 在 `GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.SourceEmission.cs` 中移除已不再参与判断的 `generationEnvironment` 透传参数
|
||||
- 调整多实例 fallback 特性发射时的换行策略,避免最后一个 fallback 特性与 `CqrsHandlerRegistryAttribute` 之间保留多余空行
|
||||
- 在 `GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs` 中补强 direct/mixed fallback 发射断言,锁定特性实例个数、拒绝空 marker,并确保 mixed 场景的程序集级 preamble 排版稳定
|
||||
- 在 `GFramework.Cqrs.Tests/Cqrs/ReflectionFallbackNotificationContainer.cs` 中为 `DirectFallbackHandlerType` 补齐 `<returns>` XML 文档
|
||||
- 本轮 review follow-up 验证已通过:
|
||||
- `dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release`
|
||||
- `0 warning / 0 error`
|
||||
- `dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"`
|
||||
- `18/18` passed
|
||||
- `dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"`
|
||||
- `13/13` passed
|
||||
|
||||
## 2026-04-20
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user