GFramework/ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
gewuyou 76fcdb8233 perf(cqrs): 拆分混合 fallback 元数据
- 优化 CqrsReflectionFallbackAttribute 与生成器发射策略,在 mixed 场景下拆分 Type 与字符串 fallback 元数据
- 补充 CQRS runtime 与 SourceGenerators 回归测试,锁定多实例 fallback 特性和定向类型回查行为
- 更新 CQRS 生成器文档与 ai-plan 恢复记录,沉淀 RP-052 的验证结果与下一步
2026-04-29 13:37:05 +08:00

13 KiB
Raw Blame History

CQRS 重写迁移追踪

2026-04-29

阶段mixed fallback 元数据拆分CQRS-REWRITE-RP-052

  • 延续 gframework-batch-boot 50Phase 8 主线,本轮把上一批的“全部可直接引用 fallback handlers 走 Type[]”继续推进到 mixed 场景
  • 先复核现状后确认:
    • CqrsHandlerRegistrar 已天然支持读取多个 CqrsReflectionFallbackAttribute 实例
    • 上一批真正阻止 mixed 场景继续收敛的点,是 runtime attribute 本身尚未开放多实例,以及 generator 只能二选一发射单个 fallback 特性
  • 已在 GFramework.Cqrs/CqrsReflectionFallbackAttribute.cs 中将特性约束改为 AllowMultiple = true,并补充注释说明多个实例的用途
  • 已在 GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs 中扩展 fallback 合同探测:
    • 探测 runtime 是否支持 params string[]
    • 探测 runtime 是否支持 params Type[]
    • 探测 runtime 是否允许多个 CqrsReflectionFallbackAttribute 实例
  • 已在 CqrsHandlerRegistryGenerator.Models.csCqrsHandlerRegistryGenerator.SourceEmission.cs 中重构 fallback 发射模型:
    • fallback 元数据现在可表示为一个或多个程序集级特性实例
    • 当 fallback handlers 全部可直接引用时,继续优先输出单个 Type[] 特性
    • 当 fallback 同时包含可直接引用与仅能按名称恢复的 handlers且 runtime 支持多实例时,拆分输出一条 Type[] 特性和一条字符串特性
    • 若 runtime 不支持多实例或缺少相应构造函数,仍整体回退到字符串元数据,避免 mixed 场景漏注册
  • 已补充 runtime 与 generator 双侧回归:
    • GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs 新增 mixed fallback metadata 用例,锁定 registrar 只对字符串条目调用一次 Assembly.GetType(...)
    • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs 新增 mixed fallback emission 用例,锁定 generator 会输出两个程序集级 fallback 特性实例
  • 同步更新:
    • GFramework.Cqrs.SourceGenerators/README.md
    • docs/zh-CN/source-generators/cqrs-handler-registry-generator.md
    • 说明 mixed 场景现在会拆分 Type 元数据与字符串元数据
  • 定向验证已通过:
    • dotnet build GFramework.Cqrs/GFramework.Cqrs.csproj -c Release
    • dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release
    • dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"
    • 13/13 passed
    • dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"
    • 18/18 passed

2026-04-20

阶段direct fallback 元数据优先级收敛CQRS-REWRITE-RP-051

  • 重新按 gframework-batch-boot 50 恢复 Phase 8 后,先复核当前 worktree 的恢复入口、origin/main 基线与分支规模:
    • worktree 仍映射到 cqrs-rewrite
    • 基线按批处理约定固定为 origin/main
    • 本轮开始前分支累计 diff 为 0 files / 0 lines
  • 结合当前代码热点与历史归档后,选择本轮批次目标为“继续收敛 generator fallback 元数据,进一步减少 runtime 按字符串类型名回查 handler 的场景”
  • 已在 GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs 中新增 runtime fallback 合同探测:
    • 识别 CqrsReflectionFallbackAttribute 是否支持 params string[]
    • 识别 CqrsReflectionFallbackAttribute 是否支持 params Type[]
  • 已在 GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.Models.csCqrsHandlerRegistryGenerator.SourceEmission.cs 中收敛 fallback 发射策略:
    • 当本轮所有 fallback handlers 都可被生成代码直接引用,且 runtime 支持 params Type[] 时,生成器现优先发射 typeof(...) 形式的程序集级 fallback 元数据
    • 当 fallback handlers 中仍存在不能直接引用的实现类型时,生成器继续整体回退到字符串元数据,避免 mixed 场景下部分 handler 走 Type[]、其余 handler 丢失恢复入口
  • 已在 GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs 补充回归:
    • 锁定 runtime 同时暴露字符串与 Type 两类 fallback 构造函数时,生成器优先选择直接 Type 元数据
    • 保留现有字符串 fallback 合同测试,确保旧 contract 兼容路径不回退
  • 同步更新:
    • GFramework.Cqrs.SourceGenerators/README.md
    • docs/zh-CN/source-generators/cqrs-handler-registry-generator.md
    • 说明“可直接引用的 fallback handlers 会优先走 typeof(...) 元数据,减少运行时字符串回查”
  • 定向验证已通过:
    • dotnet build GFramework.Cqrs.SourceGenerators/GFramework.Cqrs.SourceGenerators.csproj -c Release
    • dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"
    • 17/17 passed
  • 额外修正:
    • active tracking 中原先引用的 ai-plan/migration/CQRS_MODULE_SPLIT_PLAN.md 在当前 worktree 已不存在;本轮已移除该失效路径,后续以 active tracking / trace 作为默认恢复入口

阶段pointer / function pointer 泛型合同拒绝CQRS-REWRITE-RP-050

  • 重新执行 $gframework-pr-review 后,确认当前分支对应 PR #261,状态仍为 OPEN
  • latest reviewed commit 当前剩余 1 条 open CodeRabbit thread指向 RP-047 历史记录仍把 MakePointerType() precise registration 写成现行路径
  • 本地核对后确认该评论有效:当前 pointer / function pointer 语义已由 RP-050 收敛为 fallback / diagnostic 路径,历史追踪必须显式标注 RP-047 已废弃,避免后续恢复时误回滚到旧方案
  • 已在 GFramework.Cqrs.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs 中收紧 TryCreateRuntimeTypeReferenceCanReferenceFromGeneratedRegistry
  • pointer / function pointer 现统一视为不可精确生成的 CQRS 泛型合同,生成器会保守回退到既有 fallback / diagnostic 路径,而不再发射运行时 MakeGenericType(...) 风险代码
  • 已在 GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs 中补充输入源诊断分离,并将相关测试改为显式断言 CS0306 与 fallback / diagnostic 结果
  • 已同步修正 ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.mdRP-047 段落,明确其已被 RP-050 覆盖,且不得恢复 MakePointerType() precise registration
  • 定向验证已通过:
    • dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~Reports_Compilation_Error_And_Skips_Precise_Registration_For_Hidden_Pointer_Response|FullyQualifiedName~Reports_Diagnostic_And_Skips_Registry_When_Fallback_Metadata_Is_Required_But_Runtime_Contract_Lacks_Fallback_Attribute|FullyQualifiedName~Emits_Assembly_Level_Fallback_Metadata_When_Fallback_Is_Required_And_Runtime_Contract_Is_Available"
    • 3/3 passed
  • 扩展验证已通过:
    • dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"
    • 14/14 passed

阶段registrar duplicate mapping 索引收敛CQRS-REWRITE-RP-049

  • 已将 CqrsHandlerRegistrar 的重复 handler mapping 判定从逐条线性扫描 IServiceCollection 收敛为单次构建的本地映射索引
  • reflection fallback 或重复类型输入场景下,后续 duplicate mapping 判定改为 HashSet 命中,不再重复遍历已有服务描述符
  • GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs 已补充“程序集枚举返回重复 handler 类型时仍只注册一份映射”的回归
  • 定向验证已通过:
    • dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"
    • 11/11 passed
    • 当前沙箱限制 MSBuild named pipe因此验证在提权环境下执行

阶段registrar handler-interface 反射缓存CQRS-REWRITE-RP-048

  • 已在 CqrsHandlerRegistrar 中新增按 Type 弱键缓存的 supported handler interface 元数据reflection 注册路径现会复用已筛选且排序好的接口列表
  • 同一 handler 类型跨容器重复注册时,不再重复执行 GetInterfaces() 与支持接口筛选;缓存仍保持卸载安全,不会长期钉住 collectible 类型
  • GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs 已补充 registrar 静态缓存清理与 supported interface 缓存复用回归
  • 定向验证已通过:
    • dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~GFramework.Cqrs.Tests.Cqrs.CqrsHandlerRegistrarTests"
    • 10/10 passed
    • 当前沙箱限制 MSBuild named pipe因此验证在提权环境下执行

阶段pointer precise runtime type 覆盖扩展CQRS-REWRITE-RP-047已由 RP-050 覆盖)

  • 曾在 CqrsHandlerRegistryGenerator 中尝试补充 pointer 类型的 runtime type 递归建模与源码发射,计划通过 MakePointerType() 还原隐藏 pointer 响应类型
  • 该方案后续已被 RP-050 明确废弃pointer / function pointer 不能作为 CQRS 泛型合同的 precise registration 输入,当前实现统一回到 fallback / diagnostic 路径,不能恢复到 MakePointerType() 精确注册
  • 已同步收紧 function pointer 签名的可直接生成判定,只有当签名中的返回值与参数类型均可从 generated registry 安全引用时才走静态注册
  • 已保留含隐藏类型 function pointer handler 的 fallback / 诊断回归覆盖,确保 pointer 支持扩展不会误删原有程序集级 fallback 契约边界
  • 后续若需恢复当前 pointer / function pointer 行为,应以 RP-050 为权威记录,而不是继续沿用本阶段的旧设计假设
  • 定向验证与 CqrsHandlerRegistryGeneratorTests 全组验证均已通过:
    • dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~Generates_Precise_Service_Type_For_Hidden_Pointer_Response|FullyQualifiedName~Reports_Diagnostic_And_Skips_Registry_When_Fallback_Metadata_Is_Required_But_Runtime_Contract_Lacks_Fallback_Attribute|FullyQualifiedName~Emits_Assembly_Level_Fallback_Metadata_When_Fallback_Is_Required_And_Runtime_Contract_Is_Available"
    • 3/3 passed
    • dotnet test GFramework.SourceGenerators.Tests/GFramework.SourceGenerators.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false --filter "FullyQualifiedName~CqrsHandlerRegistryGeneratorTests"
    • 14/14 passed
    • 当前沙箱限制 MSBuild named pipe因此验证在提权环境下执行

阶段generated registry 激活反射收敛CQRS-REWRITE-RP-046

  • 已在 CqrsHandlerRegistrar 中将 generated registry 的无参构造激活改为类型级缓存工厂
  • 默认路径优先使用一次性动态方法直接创建 registry避免后续每次命中缓存仍走 ConstructorInfo.Invoke
  • 若运行环境不允许动态方法,则保留原有反射激活回退,确保 generated registry 路径不因运行时限制失效
  • 已补充“私有无参构造 generated registry 仍可激活”的回归测试,覆盖现有生成器产物兼容性
  • 定向验证已通过:
    • dotnet test GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj -c Release --no-restore -p:RestoreFallbackFolders= -m:1 -nodeReuse:false
    • 63/63 passed
    • 当前沙箱限制 MSBuild named pipe因此验证在提权环境下执行

Archive Context

  • 历史跟踪归档:
    • ai-plan/public/cqrs-rewrite/archive/todos/cqrs-rewrite-history-through-rp043.md
  • 历史 trace 归档:
    • ai-plan/public/cqrs-rewrite/archive/traces/cqrs-rewrite-history-through-rp043.md

当前下一步

  1. 回到 Phase 8 主线,优先再找一个 generator 覆盖缺口,继续减少仍需程序集级字符串 fallback 元数据的 handler 场景
  2. 若继续文档主线,优先补齐 docs/zh-CN/api-reference 与教程入口页中仍过时的 CQRS API / 命名空间表述
  3. 若后续 review thread 或 PR 状态再次变化,再重新执行 $gframework-pr-review 复核远端信号