Skip to content

Refactor/cqrs architecture decoupling todo 10#232

Merged
GeWuYou merged 7 commits into
refactor/cqrs-architecture-decouplingfrom
refactor/cqrs-architecture-decoupling-todo-10
Apr 16, 2026
Merged

Refactor/cqrs architecture decoupling todo 10#232
GeWuYou merged 7 commits into
refactor/cqrs-architecture-decouplingfrom
refactor/cqrs-architecture-decoupling-todo-10

Conversation

@GeWuYou
Copy link
Copy Markdown
Owner

@GeWuYou GeWuYou commented Apr 16, 2026

Summary by CodeRabbit

发布说明

  • 重构

    • 改进了 CQRS 调度器缓存,将缓存按响应类型划分并减少装箱,提升类型安全与性能。
    • 重构了处理程序注册生成器,改为基于编译可访问性检查并统一有序生成直接与反射注册。
  • 测试

    • 增加并修正了响应类型隔离的缓存测试,防止测试间状态泄露。
    • 扩展源生成测试支持附加元数据引用并新增生成器行为验证用例。

GeWuYou added 3 commits April 16, 2026 17:07
- 实现了 GFramework 自有 CQRS 运行时分发器,支持请求/通知/流式请求处理
- 添加了多层级缓存机制,包括服务类型缓存、调用委托缓存、按响应类型分层缓存
- 实现了上下文感知处理器的自动注入功能
- 集成了管道行为链处理机制,支持中间件模式的请求处理
- 添加了完整的缓存测试用例,验证各种消息类型的缓存命中与复用逻辑
- 优化了反射调用性能,避免热路径中的重复类型构造与装箱操作
- 修改注册条件判断逻辑,支持多种注册类型的组合处理
- 新增有序注册实现方法,统一处理直接、反射和精确反射注册
- 添加注册类型枚举以区分不同的注册方式
- 实现混合注册场景下的稳定排序机制
- 更新反射注册逻辑以支持更复杂的类型解析
- 优化代码结构提升可读性和维护性
- 添加单元测试验证各种混合注册场景的正确性
- 实现了CqrsHandlerRegistryGenerator源代码生成器
- 为CQRS处理器减少运行时程序集反射扫描开销
- 支持IRequestHandler、INotificationHandler和IStreamRequestHandler接口
- 提供静态类型引用和运行时反射发现的混合注册策略
- 生成服务注册代码并添加调试日志记录功能
- 实现精确的运行时类型引用描述和泛型类型处理
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 16, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 23686357-13ab-43d1-900d-31328b3f65ae

📥 Commits

Reviewing files that changed from the base of the PR and between eca2d67 and 1630775.

📒 Files selected for processing (5)
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
  • GFramework.SourceGenerators.Tests/GlobalUsings.cs
  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
✅ Files skipped from review due to trivial changes (1)
  • GFramework.SourceGenerators.Tests/GlobalUsings.cs
🚧 Files skipped from review as they are similar to previous changes (3)
  • GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
📜 Recent review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Code Quality & Security
  • GitHub Check: Analyze (C#)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: LoggerGenerator must automatically generate log fields and logging helper methods for classes decorated with [Log] attribute
PriorityGenerator must generate priority comparison implementations for classes decorated with [Priority] attribute
EnumExtensionsGenerator must generate enum extension capabilities for enums decorated with [GenerateEnumExtensions] attribute
ContextAwareGenerator must automatically implement IContextAware boilerplate logic for classes decorated with [ContextAware] attribute

**/*.cs: All public, protected, and internal types and members MUST include XML documentation comments (///) with <summary>, <param>, <returns>, <exception>, and <remarks> where applicable
Add inline comments for non-trivial logic, concurrency/threading behavior, performance-sensitive paths, workarounds, compatibility constraints, and edge cases
Core framework components (Architecture, Module, System, Context, Registry, Service Module, Lifecycle types) MUST include high-level explanations of responsibilities, lifecycle, interactions, and design rationale
Generated logic and source generator pipelines MUST explain what is generated, why it is generated, semantic assumptions, and diagnostic or fallback behavior
Methods with non-trivial logic MUST document the core idea, key decisions, and edge case handling
Do not rely on implicit imports; declare every required using explicitly in C# files
Write null-safe code that respects nullable annotations instead of suppressing warnings by default
Use the namespace pattern GFramework.{Module}.{Feature} with PascalCase segments
Follow standard C# naming: Types, methods, properties, events, constants use PascalCase; interfaces use I prefix; parameters and locals use camelCase; private fields use _camelCase
Use 4 spaces for indentation (not tabs), use Allman braces, and keep using directives at the top of the file sorted consistently
Prefer one primary type per file unless the surrounding project alread...

Files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
**/*.{cs,csproj}

📄 CodeRabbit inference engine (AGENTS.md)

Every non-trivial feature, bug fix, or behavior change MUST include tests or an explicit justification for why a test is not practical

Files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
**/*SourceGenerators/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

Keep source generators deterministic and free of hidden environment or network dependencies

Files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T15:30:46.211Z
Learning: Implement CQRS pattern (Command Query Responsibility Segregation) with support for both synchronous and asynchronous execution, using Mediator pattern integrated via source code generators
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.cs : Preserve deterministic behavior in registries, lifecycle orchestration, and generated outputs

Applied to files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.cs : Avoid hidden side effects in property getters, constructors, and registration helpers

Applied to files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*SourceGenerators/**/*.cs : Keep source generators deterministic and free of hidden environment or network dependencies

Applied to files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
📚 Learning: 2026-04-06T12:45:43.921Z
Learnt from: GeWuYou
Repo: GeWuYou/GFramework PR: 190
File: GFramework.Game/Config/GameConfigBootstrap.cs:1-3
Timestamp: 2026-04-06T12:45:43.921Z
Learning: In the GeWuYou/GFramework repository, C# files may omit explicit `using System*` imports because the project-wide `GlobalUsings.cs` (referenced via manual global `using` directives) supplies common namespaces (e.g., `System`, `System.Threading`, `System.Threading.Tasks`). During code review, do not flag missing `using System...` directives in `.cs` files as long as `GlobalUsings.cs` is present/used to provide those namespaces.

Applied to files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
🔇 Additional comments (9)
GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs (9)

89-141: LGTM!

重构逻辑清晰:将可访问性检查委托给 compilation.IsSymbolAccessibleWithin(),并引入 requiresRuntimeInterfaceDiscovery 标志实现"已知接口静态注册 + 剩余接口运行时补洞"的混合策略。注释(第127-129行)清楚地解释了设计意图。


390-416: LGTM!

使用 compilation.IsSymbolAccessibleWithin() 进行可访问性检查是正确的做法,递归验证泛型类型参数确保了完整的可访问性检查。默认分支的注释(第412-414行)解释了为何对其他类型持宽容态度,便于未来针对具体问题收紧。


587-748: LGTM!

有序注册合并逻辑设计良好:

  1. 将三种注册类型统一排序(使用 StringComparer.Ordinal)确保生成代码的确定性
  2. 使用 HashSet<Type> 跟踪已注册的服务类型,避免运行时发现阶段产生重复注册
  3. 第732-734行的 InvalidOperationException 是良好的防御性编码

Based on learnings: "Preserve deterministic behavior in registries, lifecycle orchestration, and generated outputs"


907-934: LGTM!

RegisterRemainingReflectedHandlerInterfaces 的签名更新合理:

  • 接受 ISet<Type> 参数用于跟踪已知服务类型
  • 第922-924行的 Contains 检查避免重复注册
  • 第932行将新发现的接口添加到集合中,确保后续调用也能正确跳过

这实现了静态注册与运行时发现的无缝衔接。


1039-1044: LGTM!

新增的 OrderedRegistrationKind 枚举清晰地表达了三种注册类型,用于有序注册逻辑中的分派。


1086-1200: LGTM!

RequiresRuntimeInterfaceDiscovery 字段已正确集成到:

  • ImplementationRegistrationSpec 记录类型
  • HandlerCandidateAnalysis 结构体的 EqualsGetHashCode 实现

这对增量生成器的缓存机制至关重要——确保当此标志变化时能正确触发重新生成。


750-845: LGTM!

AppendPreciseReflectedTypeResolution 的更新正确地将 knownServiceTypes 跟踪逻辑整合进来,确保精确反射注册的服务类型也被记录(第824-831行)。


522-532: LGTM!

registryAssembly 变量仅在需要反射查找时才生成,避免了不必要的代码输出。


564-585: LGTM!

AppendDirectRegistrations 方法提取得当,简化了直接注册的生成逻辑。


📝 Walkthrough

Walkthrough

重构 CQRS 调度器的缓存为按响应类型作用域的嵌套泛型缓存;更新相关单元测试以清理并验证按响应类型的委托缓存;并改进处理程序注册源生成器以基于 Roslyn Compilation 做可访问性检查与运行时接口发现,新增测试/辅助构建器支持 MetadataReference。

Changes

Cohort / File(s) Summary
CQRS 调度器实现
GFramework.Cqrs/Internal/CqrsDispatcher.cs
移除进程级 (requestType,responseType) 缓存,新增 RequestInvokerCache<TResponse> / RequestPipelineInvokerCache<TResponse> 嵌套泛型缓存;请求/流水线委托由 ValueTask<object?> 改为 ValueTask<TResponse>;反射/委托创建改为泛型化。
调度器缓存测试
GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
为测试添加 dispatcher 类型解析与嵌套泛型缓存读取辅助方法;新增 ClearDispatcherCaches() 并在 SetUp() 调用以避免测试间状态泄漏;更新现有断言并新增 Dispatcher_Should_Cache_Request_Invokers_Per_Response_Type 测试及辅助请求/处理程序类型。
源生成器 测试基础设施
GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs, GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs
为 GeneratorTest.RunAsync 添加接受 IEnumerable<MetadataReference> 的重载;新增 MetadataReferenceTestBuilder,支持从字符串源构建内存中 MetadataReference 并提供运行时程序集引用缓存与错误诊断。
处理程序注册生成器
GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
将可引用性检查切换为基于 Compilation.IsSymbolAccessibleWithin 的实现,改为在分析中记录 RequiresRuntimeInterfaceDiscovery 并在生成阶段统一按序发出直接、反射及精确反射注册,新增 RegisterRemainingReflectedHandlerInterfaces 路径与已知接口跟踪以避免重复注册。
处理程序注册生成器测试
GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs, GFramework.SourceGenerators.Tests/GlobalUsings.cs
新增多个测试用例验证混合可见/不可见接口的生成结果与运行时代查找路径;添加 RunGenerator(...) 辅助方法并引入对 Microsoft.CodeAnalysis.CSharp 的全局 using。

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Caller as Caller
    participant Dispatcher as CqrsDispatcher
    participant Cache as RequestInvokerCache<TResponse>
    participant Handler as Handler<TResponse>

    Caller->>Dispatcher: Dispatch(request: IRequest<TResponse>)
    Dispatcher->>Cache: lookup by request.Type
    alt invoker exists
        Cache-->>Dispatcher: invoker delegate(ValueTask<TResponse>)
    else create invoker
        Dispatcher->>Dispatcher: CreateRequestInvoker<TResponse>(requestType)
        Dispatcher->>Cache: store invoker
    end
    Dispatcher->>Handler: invoke delegate -> ValueTask<TResponse>
    Handler-->>Dispatcher: ValueTask<TResponse> result
    Dispatcher-->>Caller: return ValueTask<TResponse>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 分钟

Possibly related PRs

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/cqrs-architecture-decoupling-todo-10

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (4)
GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs (1)

155-168: 建议:考虑添加对 string 类型 pipeline 缓存的清理。

ClearDispatcherCaches() 清理了 RequestPipelineInvokerCache<int> 但未清理 RequestPipelineInvokerCache<string>。虽然当前测试未涉及 string 类型的 pipeline 路径,但为保持一致性和防止未来扩展测试时出现意外状态泄漏,建议补充。

♻️ 建议的改进
     private static void ClearDispatcherCaches()
     {
         GetCacheField("NotificationHandlerServiceTypes").Clear();
         GetCacheField("RequestServiceTypes").Clear();
         GetCacheField("StreamHandlerServiceTypes").Clear();
         GetCacheField("NotificationInvokers").Clear();
         GetCacheField("StreamInvokers").Clear();
         GetGenericCacheField("RequestInvokerCache`1", typeof(int), "Invokers").Clear();
         GetGenericCacheField("RequestInvokerCache`1", typeof(string), "Invokers").Clear();
         GetGenericCacheField("RequestPipelineInvokerCache`1", typeof(int), "Invokers").Clear();
+        GetGenericCacheField("RequestPipelineInvokerCache`1", typeof(string), "Invokers").Clear();
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` around lines 155 -
168, ClearDispatcherCaches() currently clears RequestPipelineInvokerCache`1 for
typeof(int) but misses the typeof(string) variant; add a call to
GetGenericCacheField("RequestPipelineInvokerCache`1", typeof(string),
"Invokers").Clear() alongside the existing GetGenericCacheField calls so the
RequestPipelineInvokerCache<string> entries are also wiped; update the
ClearDispatcherCaches method to include this new Clear invocation using the same
GetGenericCacheField helper.
GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs (1)

390-414: 递归可访问性检查逻辑清晰,但 default 分支可能过于宽松。

CanReferenceFromGeneratedRegistry 方法对数组、命名类型、指针和类型参数的处理正确。但 default 分支返回 true 可能会对未知类型种类(如 IFunctionPointerTypeSymbolIDynamicTypeSymbol 等)过于宽松。

建议在 default 分支添加注释说明假设,或考虑更保守的处理方式。

♻️ 建议:为 default 分支添加说明注释
             case ITypeParameterSymbol:
                 return false;
             default:
+                // 对于其他类型种类(如 dynamic、error types 等),假设可引用。
+                // 如果遇到实际问题,可在此处添加更保守的处理。
                 return true;
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs` around
lines 390 - 414, The default branch of CanReferenceFromGeneratedRegistry
currently returns true and may be too permissive for unknown symbol kinds (e.g.
IFunctionPointerTypeSymbol, IDynamicTypeSymbol); update the default branch in
CanReferenceFromGeneratedRegistry to be conservative by either explicitly
handling additional symbol kinds or returning false for unhandled symbol kinds,
and add a clear comment explaining the assumption and rationale (mentioning
IFunctionPointerTypeSymbol and IDynamicTypeSymbol) so future readers know why a
conservative default was chosen.
GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs (1)

1-65: 整体实现良好,符合测试辅助类的设计预期。

代码结构清晰,XML 文档完整,符合编码规范。两个方法的职责明确:

  • CreateFromSource 负责将源码编译为内存元数据引用
  • GetRuntimeMetadataReferences 负责获取运行时可信平台程序集引用

有一个小的优化建议:GetRuntimeMetadataReferences() 每次调用都会重新解析 TRUSTED_PLATFORM_ASSEMBLIES 并创建引用集合。如果测试场景中频繁调用 CreateFromSource,可考虑将运行时引用缓存为静态字段以提升性能。

♻️ 可选:缓存运行时元数据引用
 public static class MetadataReferenceTestBuilder
 {
+    private static readonly Lazy<ImmutableArray<MetadataReference>> CachedRuntimeReferences =
+        new(static () =>
+        {
+            var trustedPlatformAssemblies = ((string?)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES"))?
+                .Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries)
+                ?? Array.Empty<string>();
+
+            return trustedPlatformAssemblies
+                .Select(static path => (MetadataReference)MetadataReference.CreateFromFile(path))
+                .ToImmutableArray();
+        });
+
     public static MetadataReference CreateFromSource(
         string assemblyName,
         string source,
         params MetadataReference[] additionalReferences)
     {
         var syntaxTree = CSharpSyntaxTree.ParseText(source);
-        var references = GetRuntimeMetadataReferences()
+        var references = CachedRuntimeReferences.Value
             .Concat(additionalReferences)
             .ToImmutableArray();
         // ...
     }

     public static ImmutableArray<MetadataReference> GetRuntimeMetadataReferences()
     {
-        var trustedPlatformAssemblies = ((string?)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES"))?
-                                        .Split(Path.PathSeparator, StringSplitOptions.RemoveEmptyEntries)
-                                        ?? Array.Empty<string>();
-
-        return trustedPlatformAssemblies
-            .Select(static path => (MetadataReference)MetadataReference.CreateFromFile(path))
-            .ToImmutableArray();
+        return CachedRuntimeReferences.Value;
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs`
around lines 1 - 65, GetRuntimeMetadataReferences currently reparses
TRUSTED_PLATFORM_ASSEMBLIES and recreates MetadataReference instances on every
call which is wasteful; cache the result in a static readonly/immutable field
(e.g., add a private static readonly ImmutableArray<MetadataReference>
s_runtimeMetadataReferences or a Lazy<ImmutableArray<MetadataReference>>) and
have GetRuntimeMetadataReferences return that cached value; update
CreateFromSource to continue calling GetRuntimeMetadataReferences but benefit
from the cached references; ensure thread-safety by initializing the cache at
type initialization or using Lazy<T>.
GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs (1)

1132-1165: RunGenerator 辅助方法实现正确,但可增强诊断信息。

方法逻辑完整:编译源码、运行生成器、验证无编译错误、返回生成的源码。

建议增强 Line 1158 的断言消息,加入更多上下文信息以便调试失败场景。

♻️ 建议:增强编译错误断言消息
         var compilationErrors = updatedCompilation.GetDiagnostics()
             .Where(static diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)
             .ToArray();
-        Assert.That(compilationErrors, Is.Empty, string.Join(Environment.NewLine, compilationErrors));
+        Assert.That(
+            compilationErrors,
+            Is.Empty,
+            () => $"编译生成的代码时出现错误:{Environment.NewLine}{string.Join(Environment.NewLine, compilationErrors.Select(d => d.ToString()))}");
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs`
around lines 1132 - 1165, The assertion that compilationErrors is empty in
RunGenerator should include richer diagnostic context for failures; update the
Assert.That call (referencing RunGenerator and the compilationErrors local) to
include a message that concatenates the diagnostics (e.g.,
string.Join(Environment.NewLine, compilationErrors)) plus relevant context such
as the input source string and possibly updatedCompilation.GetDiagnostics()
output so test failures show the source and full diagnostic text to aid
debugging.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs`:
- Around line 155-168: ClearDispatcherCaches() currently clears
RequestPipelineInvokerCache`1 for typeof(int) but misses the typeof(string)
variant; add a call to GetGenericCacheField("RequestPipelineInvokerCache`1",
typeof(string), "Invokers").Clear() alongside the existing GetGenericCacheField
calls so the RequestPipelineInvokerCache<string> entries are also wiped; update
the ClearDispatcherCaches method to include this new Clear invocation using the
same GetGenericCacheField helper.

In `@GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs`:
- Around line 1-65: GetRuntimeMetadataReferences currently reparses
TRUSTED_PLATFORM_ASSEMBLIES and recreates MetadataReference instances on every
call which is wasteful; cache the result in a static readonly/immutable field
(e.g., add a private static readonly ImmutableArray<MetadataReference>
s_runtimeMetadataReferences or a Lazy<ImmutableArray<MetadataReference>>) and
have GetRuntimeMetadataReferences return that cached value; update
CreateFromSource to continue calling GetRuntimeMetadataReferences but benefit
from the cached references; ensure thread-safety by initializing the cache at
type initialization or using Lazy<T>.

In `@GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs`:
- Around line 1132-1165: The assertion that compilationErrors is empty in
RunGenerator should include richer diagnostic context for failures; update the
Assert.That call (referencing RunGenerator and the compilationErrors local) to
include a message that concatenates the diagnostics (e.g.,
string.Join(Environment.NewLine, compilationErrors)) plus relevant context such
as the input source string and possibly updatedCompilation.GetDiagnostics()
output so test failures show the source and full diagnostic text to aid
debugging.

In `@GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs`:
- Around line 390-414: The default branch of CanReferenceFromGeneratedRegistry
currently returns true and may be too permissive for unknown symbol kinds (e.g.
IFunctionPointerTypeSymbol, IDynamicTypeSymbol); update the default branch in
CanReferenceFromGeneratedRegistry to be conservative by either explicitly
handling additional symbol kinds or returning false for unhandled symbol kinds,
and add a clear comment explaining the assumption and rationale (mentioning
IFunctionPointerTypeSymbol and IDynamicTypeSymbol) so future readers know why a
conservative default was chosen.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a0dac618-83c3-4a42-ae22-f43e992b0b98

📥 Commits

Reviewing files that changed from the base of the PR and between 692671a and eca2d67.

📒 Files selected for processing (6)
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs
  • GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Code Quality & Security
  • GitHub Check: Analyze (C#)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: LoggerGenerator must automatically generate log fields and logging helper methods for classes decorated with [Log] attribute
PriorityGenerator must generate priority comparison implementations for classes decorated with [Priority] attribute
EnumExtensionsGenerator must generate enum extension capabilities for enums decorated with [GenerateEnumExtensions] attribute
ContextAwareGenerator must automatically implement IContextAware boilerplate logic for classes decorated with [ContextAware] attribute

**/*.cs: All public, protected, and internal types and members MUST include XML documentation comments (///) with <summary>, <param>, <returns>, <exception>, and <remarks> where applicable
Add inline comments for non-trivial logic, concurrency/threading behavior, performance-sensitive paths, workarounds, compatibility constraints, and edge cases
Core framework components (Architecture, Module, System, Context, Registry, Service Module, Lifecycle types) MUST include high-level explanations of responsibilities, lifecycle, interactions, and design rationale
Generated logic and source generator pipelines MUST explain what is generated, why it is generated, semantic assumptions, and diagnostic or fallback behavior
Methods with non-trivial logic MUST document the core idea, key decisions, and edge case handling
Do not rely on implicit imports; declare every required using explicitly in C# files
Write null-safe code that respects nullable annotations instead of suppressing warnings by default
Use the namespace pattern GFramework.{Module}.{Feature} with PascalCase segments
Follow standard C# naming: Types, methods, properties, events, constants use PascalCase; interfaces use I prefix; parameters and locals use camelCase; private fields use _camelCase
Use 4 spaces for indentation (not tabs), use Allman braces, and keep using directives at the top of the file sorted consistently
Prefer one primary type per file unless the surrounding project alread...

Files:

  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs
  • GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
**/*.{cs,csproj}

📄 CodeRabbit inference engine (AGENTS.md)

Every non-trivial feature, bug fix, or behavior change MUST include tests or an explicit justification for why a test is not practical

Files:

  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs
  • GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
**/*SourceGenerators/**/*.cs

📄 CodeRabbit inference engine (AGENTS.md)

Keep source generators deterministic and free of hidden environment or network dependencies

Files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
🧠 Learnings (14)
📚 Learning: 2026-04-06T12:45:43.921Z
Learnt from: GeWuYou
Repo: GeWuYou/GFramework PR: 190
File: GFramework.Game/Config/GameConfigBootstrap.cs:1-3
Timestamp: 2026-04-06T12:45:43.921Z
Learning: In the GeWuYou/GFramework repository, C# files may omit explicit `using System*` imports because the project-wide `GlobalUsings.cs` (referenced via manual global `using` directives) supplies common namespaces (e.g., `System`, `System.Threading`, `System.Threading.Tasks`). During code review, do not flag missing `using System...` directives in `.cs` files as long as `GlobalUsings.cs` is present/used to provide those namespaces.

Applied to files:

  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs
  • GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.Tests/*.cs : Source generator changes MUST be covered by generator tests; preserve snapshot-based verification patterns already used in the repository

Applied to files:

  • GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs
  • GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs
  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.Tests/*.cs : When generator behavior changes intentionally, update snapshots together with the implementation

Applied to files:

  • GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*SourceGenerators/**/*.cs : Keep source generators deterministic and free of hidden environment or network dependencies

Applied to files:

  • GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs
  • GFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.cs
  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-05T15:30:46.211Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T15:30:46.211Z
Learning: IContextAware must provide unified context access through SetContext(IArchitectureContext) method for components to obtain architecture context

Applied to files:

  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.cs : Preserve deterministic behavior in registries, lifecycle orchestration, and generated outputs

Applied to files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.cs : Generated logic and source generator pipelines MUST explain what is generated, why it is generated, semantic assumptions, and diagnostic or fallback behavior

Applied to files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.cs : Avoid hidden side effects in property getters, constructors, and registration helpers

Applied to files:

  • GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.cs : Public API changes must be covered by unit or integration tests

Applied to files:

  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.Tests/*.cs : When a public API defines multiple contract branches, tests MUST cover meaningful variants including null, empty, default, and filtered inputs when those branches change behavior

Applied to files:

  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.Tests/*.cs : Keep tests focused on observable behavior, not implementation trivia

Applied to files:

  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.Tests/*.cs : Regression fixes should include a test that fails before the fix and passes after it

Applied to files:

  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-14T01:59:40.041Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-04-14T01:59:40.041Z
Learning: Applies to **/*.cs : Use the namespace pattern `GFramework.{Module}.{Feature}` with PascalCase segments

Applied to files:

  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
📚 Learning: 2026-04-05T15:30:46.211Z
Learnt from: CR
Repo: GeWuYou/GFramework PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-05T15:30:46.211Z
Learning: Applies to **/*.cs : ContextAwareGenerator must automatically implement IContextAware boilerplate logic for classes decorated with [ContextAware] attribute

Applied to files:

  • GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs
🔇 Additional comments (15)
GFramework.Cqrs/Internal/CqrsDispatcher.cs (4)

314-330: 静态泛型缓存设计合理,LGTM。

使用嵌套静态泛型类 RequestInvokerCache<TResponse>RequestPipelineInvokerCache<TResponse> 按响应类型分层缓存调用委托,有效避免了值类型响应在 object 桥接中的装箱开销。这是一种常见且高效的 per-type 缓存模式。

XML 文档注释清晰解释了设计意图。


124-135: 请求分发路径改造正确。

SendAsync 方法现在根据 TResponse 类型从对应的泛型缓存中获取或创建调用委托,直接返回强类型结果,消除了之前的装箱/拆箱路径。


191-208: 委托创建逻辑正确泛型化。

CreateRequestInvoker<TResponse>CreateRequestPipelineInvoker<TResponse> 方法正确地将 typeof(TResponse) 传递给 MakeGenericMethod,生成与响应类型匹配的强类型委托。


233-268: 调用助手方法签名更新正确。

InvokeRequestHandlerAsyncInvokeRequestPipelineAsync 现在直接返回 ValueTask<TResponse>,与上层委托类型定义保持一致,避免了不必要的类型转换。

GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs (4)

108-136: 新测试有效验证了按响应类型分层缓存的行为,LGTM。

Dispatcher_Should_Cache_Request_Invokers_Per_Response_Type 测试清晰地验证了:

  1. IRequest<int>IRequest<string> 各自拥有独立的缓存条目
  2. 重复分发不会导致缓存条目增长

测试逻辑完整,断言明确。


170-194: 反射辅助方法实现正确。

GetGenericCacheField 正确处理了嵌套泛型类型的反射访问流程:查找嵌套类型 → 构造闭合泛型类型 → 获取静态字段。断言信息清晰,便于调试。


236-312: 新增测试类型定义完整。

DispatcherStringCacheRequestDispatcherStringCacheRequestHandler 为验证 string 响应类型的缓存路径提供了必要的测试基础设施,XML 文档齐全。


18-33: 测试初始化中清理静态缓存是正确做法。

SetUp 中调用 ClearDispatcherCaches() 确保了每个测试用例从干净状态开始,避免了进程级静态缓存在并行或顺序执行测试时导致的状态泄漏问题。

GFramework.SourceGenerators.Tests/Core/GeneratorTest.cs (1)

16-56: LGTM!

新增的重载方法设计合理,保持了向后兼容性。原有方法委托到新方法的模式清晰,additionalReferences 参数支持多程序集测试场景的需求。

GFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.cs (3)

585-746: AppendOrderedImplementationRegistrations 方法实现完整,确保了混合注册场景的稳定排序。

方法逻辑清晰:

  1. 收集所有注册类型(Direct、ReflectedImplementation、PreciseReflected)
  2. HandlerInterfaceLogName 排序确保确定性输出
  3. 根据 RequiresRuntimeInterfaceDiscovery 决定是否追踪已知服务类型
  4. 最后调用 RegisterRemainingReflectedHandlerInterfaces 处理剩余接口

1037-1042: 新增 OrderedRegistrationKind 枚举清晰地区分了注册类型。

枚举设计简洁,三种类型覆盖了所有注册场景。


1084-1092: ImplementationRegistrationSpec 记录新增字段设计合理。

RequiresRuntimeInterfaceDiscovery 字段的添加与 HandlerCandidateAnalysis 保持一致,确保了数据在分析和生成阶段的正确传递。

GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs (3)

168-254: 新增的预期输出常量正确覆盖了混合注册场景。

MixedDirectAndPreciseRegistrationsExpectedMixedReflectedImplementationAndPreciseRegistrationsExpected 两个常量清晰地展示了:

  • 直接注册与精确重建注册的混合输出
  • 隐藏实现类型的反射解析
  • Handler 接口按名称稳定排序

671-827: 混合注册场景的测试覆盖完整。

两个测试方法分别验证了:

  1. Generates_Mixed_Direct_And_Precise_Registrations_For_Same_Implementation:可见实现 + 混合接口
  2. Generates_Mixed_Reflected_Implementation_And_Precise_Registrations_For_Same_Implementation:隐藏实现 + 混合接口

测试数据设计合理,覆盖了生成器的关键行为边界。


829-956: 外部 protected 类型的部分运行时接口发现测试设计良好。

测试通过多程序集场景验证了:

  • 生成器正确处理继承自外部程序集的 handler
  • 可访问接口被直接注册
  • 不可访问的 protected 类型接口回退到运行时发现
  • knownServiceTypes 集合正确追踪已注册接口

Assert.Multiple 的使用确保了所有断言都会执行,便于问题诊断。

GeWuYou added 4 commits April 16, 2026 19:06
- 实现CqrsHandlerRegistryGenerator源代码生成器
- 减少运行时程序集反射扫描开销
- 支持IRequestHandler、INotificationHandler和IStreamRequestHandler接口
- 生成静态注册代码替代运行时动态发现
- 提供精确的运行时类型引用描述功能
- 实现泛型类型和数组类型的反射处理
- 添加日志记录和错误处理机制
- 支持跨程序集类型引用和内部类型反射查找
- 生成符合IServiceCollection的服务注册代码
- 验证相同消息类型重复分发时不会重复扩张服务类型与调用委托缓存
- 验证 request 调用委托会按响应类型分别缓存避免不同响应类型共用 object 结果桥接
- 实现通过反射读取 dispatcher 静态缓存字典的测试辅助方法
- 添加清空 dispatcher 静态缓存的方法避免跨用例共享进程级状态
- 创建多个测试数据模型和处理器用于验证不同的缓存场景
- 实现异步流消费方法确保建流路径被真实执行
- 创建 MetadataReferenceTestBuilder 工具类用于构建内存元数据引用
- 实现 CreateFromSource 方法将源码编译为内存程序集并返回元数据引用
- 添加 GetRuntimeMetadataReferences 方法获取当前运行时可信平台程序集引用
- 创建 CqrsHandlerRegistryGeneratorTests 测试类验证 CQRS 处理器注册生成器功能
- 添加多种测试用例验证不同场景下的处理器注册行为
- 包含嵌套处理器、隐藏实现、数组类型参数、泛型类型定义等边界情况测试
- 实现混合直接注册和精确重建注册的测试验证
- 添加对外部基类保护类型处理器的支持测试
- 验证生成器优先处理隐藏处理器而不输出遗留回退标记的功能
- 将 Microsoft.CodeAnalysis.CSharp 引用添加到 GlobalUsings.cs
- 重新排列引用顺序以符合代码风格规范
@GeWuYou GeWuYou merged commit e96623b into refactor/cqrs-architecture-decoupling Apr 16, 2026
7 checks passed
@GeWuYou GeWuYou deleted the refactor/cqrs-architecture-decoupling-todo-10 branch April 16, 2026 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant