--- title: CQRS Handler Registry 生成器 description: 为消费端程序集生成 CQRS handler registry,并在需要时附带精确 reflection fallback 元数据。 --- # CQRS Handler Registry 生成器 `GFramework.Cqrs.SourceGenerators` 会在编译期为当前业务程序集生成 `ICqrsHandlerRegistry`,让 `GFramework.Cqrs` runtime 在注册 handlers 时优先走静态注册表,而不是先扫描整个程序集。 它服务的是 `Cqrs` 家族,不是独立运行时: - 契约层:`GeWuYou.GFramework.Cqrs.Abstractions` - 默认 runtime:`GeWuYou.GFramework.Cqrs` - 编译期生成器:`GeWuYou.GFramework.Cqrs.SourceGenerators` ## 生成什么 当前生成器会分析消费端程序集中的: - `IRequestHandler<,>` - `INotificationHandler<>` - `IStreamRequestHandler<,>` 然后输出两类结果: 1. 一个实现 `ICqrsHandlerRegistry` 的内部注册器类型 2. 程序集级 `CqrsHandlerRegistryAttribute` 当某些 handler 不能被生成代码安全地直接引用时,还会补发: - 程序集级 `CqrsReflectionFallbackAttribute` 这意味着运行时会先使用生成注册器完成可静态表达的映射,再只对剩余类型做补扫,而不是退回整程序集盲扫。 ## 最小接入路径 安装方式保持 runtime 包与生成器包版本一致,并把生成器作为编译期依赖引入: ```xml ``` 运行时侧仍然按 `Core` 的标准入口注册程序集: ```csharp protected override void OnInitialize() { RegisterCqrsHandlersFromAssembly(typeof(GameArchitecture).Assembly); } ``` 如果你的 handlers 分布在多个业务程序集里,则改用: ```csharp RegisterCqrsHandlersFromAssemblies( [ typeof(InventoryCqrsMarker).Assembly, typeof(BattleCqrsMarker).Assembly ]); ``` 文档示例统一用 marker 类型承载程序集引用。框架本身不要求固定目录或固定命名,但团队实践里可以把这类空 marker 集中放在每个业务程序集自己的 `Application/Markers` 或等价目录,并采用 `InventoryCqrsMarker` 这类能直接看出来源 的名字,避免多人协作时拿无关业务类型充当程序集定位锚点。 ## 运行时如何消费生成结果 `Cqrs` runtime 当前的注册顺序是: 1. 先读取程序集上的 `CqrsHandlerRegistryAttribute` 2. 优先激活生成的 `ICqrsHandlerRegistry` 3. 若生成元数据损坏、registry 不可激活,记录告警并回退到反射路径 4. 若存在 `CqrsReflectionFallbackAttribute`,只补扫剩余 handler 5. 同一程序集按稳定键去重,避免重复注册 这个行为由 `GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs` 和 `GFramework.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 合同不足以安全承载这轮生成结果”。 ## 源码与 API 阅读入口 如果你要核对生成器对外暴露的契约,优先看这些类型: - `GFramework.Cqrs.ICqrsHandlerRegistry` - `GFramework.Cqrs.CqrsHandlerRegistryAttribute` - `GFramework.Cqrs.CqrsReflectionFallbackAttribute` - `GFramework.Cqrs.SourceGenerators.Cqrs.CqrsHandlerRegistryGenerator` 模块族入口见: - [CQRS 运行时](../core/cqrs.md) - [源码生成器总览](./index.md)