GFramework/docs/zh-CN/source-generators/cqrs-handler-registry-generator.md
gewuyou a1dbed3c8d docs(cqrs): 刷新 Cqrs 文档入口
- 更新 Cqrs family landing、API 参考与 source-generators 导航

- 新增 CQRS handler registry 专题页并补充 XML inventory

- 补充 runtime 与 generator 内部类型 XML 注释

- 记录 RP-004 验证结果与后续恢复点
2026-04-22 18:51:05 +08:00

4.2 KiB
Raw Blame History

title, description
title description
CQRS Handler Registry 生成器 为消费端程序集生成 CQRS handler registry并在需要时附带精确 reflection fallback 元数据。

CQRS Handler Registry 生成器

GFramework.Cqrs.SourceGenerators 会在编译期为当前业务程序集生成 ICqrsHandlerRegistry,让 GFramework.Cqrs runtime 在注册 handlers 时优先走静态注册表,而不是先扫描整个程序集。

它服务的是 Cqrs 家族,不是独立运行时:

  • 契约层:GeWuYou.GFramework.Cqrs.Abstractions
  • 默认 runtimeGeWuYou.GFramework.Cqrs
  • 编译期生成器:GeWuYou.GFramework.Cqrs.SourceGenerators

生成什么

当前生成器会分析消费端程序集中的:

  • IRequestHandler<,>
  • INotificationHandler<>
  • IStreamRequestHandler<,>

然后输出两类结果:

  1. 一个实现 ICqrsHandlerRegistry 的内部注册器类型
  2. 程序集级 CqrsHandlerRegistryAttribute

当某些 handler 不能被生成代码安全地直接引用时,还会补发:

  • 程序集级 CqrsReflectionFallbackAttribute

这意味着运行时会先使用生成注册器完成可静态表达的映射,再只对剩余类型做补扫,而不是退回整程序集盲扫。

最小接入路径

安装方式保持 runtime 包与生成器包版本一致,并把生成器作为编译期依赖引入:

<ItemGroup>
  <PackageReference Include="GeWuYou.GFramework.Cqrs" Version="x.y.z" />
  <PackageReference Include="GeWuYou.GFramework.Cqrs.Abstractions" Version="x.y.z" />
  <PackageReference Include="GeWuYou.GFramework.Cqrs.SourceGenerators"
                    Version="x.y.z"
                    PrivateAssets="all"
                    ExcludeAssets="runtime" />
</ItemGroup>

运行时侧仍然按 Core 的标准入口注册程序集:

protected override void OnInitialize()
{
    RegisterCqrsHandlersFromAssembly(typeof(GameArchitecture).Assembly);
}

如果你的 handlers 分布在多个业务程序集里,则改用:

RegisterCqrsHandlersFromAssemblies(
[
    typeof(InventoryCqrsMarker).Assembly,
    typeof(BattleCqrsMarker).Assembly
]);

运行时如何消费生成结果

Cqrs runtime 当前的注册顺序是:

  1. 先读取程序集上的 CqrsHandlerRegistryAttribute
  2. 优先激活生成的 ICqrsHandlerRegistry
  3. 若生成元数据损坏、registry 不可激活,记录告警并回退到反射路径
  4. 若存在 CqrsReflectionFallbackAttribute,只补扫剩余 handler
  5. 同一程序集按稳定键去重,避免重复注册

这个行为由 GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.csGFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs 共同覆盖。

什么时候值得安装

推荐安装:

  • 业务程序集内 handler 数量较多
  • 想把 handler 注册路径前移到编译期
  • 希望冷启动阶段减少整程序集反射扫描
  • 需要更明确地观察“哪些 handler 走静态注册,哪些只能走 fallback”

可以先不装:

  • 项目体量很小handler 很少
  • 当前只做原型,尚不关心注册成本
  • 你还没稳定到 Cqrs runtime 的最终接入边界

fallback 边界

生成器并不会承诺“所有 handler 都能被静态表达”。

当前实现遵循一个保守原则:

  • 能直接引用的 handler生成直接注册语句
  • 实现类型不能直接引用、但服务接口还能精确表达时,生成反射实现类型查找
  • 服务接口本身也需要运行时解析时,生成精确 type lookup
  • 只有在 runtime 提供 CqrsReflectionFallbackAttribute 合同时,才允许发射依赖 fallback 的结果

如果当前编译环境缺少这个 fallback 合同,而某些 handler 又必须依赖它,生成器会报:

  • GF_Cqrs_001

这条诊断的含义不是“某个 handler 写错了”,而是“当前 runtime 合同不足以安全承载这轮生成结果”。

XML / API 阅读入口

如果你要核对生成器对外暴露的契约,优先看这些类型:

  • GFramework.Cqrs.ICqrsHandlerRegistry
  • GFramework.Cqrs.CqrsHandlerRegistryAttribute
  • GFramework.Cqrs.CqrsReflectionFallbackAttribute
  • GFramework.Cqrs.SourceGenerators.Cqrs.CqrsHandlerRegistryGenerator

模块族入口见: