Refactor/cqrs architecture decoupling todo 10#232
Conversation
- 实现了 GFramework 自有 CQRS 运行时分发器,支持请求/通知/流式请求处理 - 添加了多层级缓存机制,包括服务类型缓存、调用委托缓存、按响应类型分层缓存 - 实现了上下文感知处理器的自动注入功能 - 集成了管道行为链处理机制,支持中间件模式的请求处理 - 添加了完整的缓存测试用例,验证各种消息类型的缓存命中与复用逻辑 - 优化了反射调用性能,避免热路径中的重复类型构造与装箱操作
- 修改注册条件判断逻辑,支持多种注册类型的组合处理 - 新增有序注册实现方法,统一处理直接、反射和精确反射注册 - 添加注册类型枚举以区分不同的注册方式 - 实现混合注册场景下的稳定排序机制 - 更新反射注册逻辑以支持更复杂的类型解析 - 优化代码结构提升可读性和维护性 - 添加单元测试验证各种混合注册场景的正确性
- 实现了CqrsHandlerRegistryGenerator源代码生成器 - 为CQRS处理器减少运行时程序集反射扫描开销 - 支持IRequestHandler、INotificationHandler和IStreamRequestHandler接口 - 提供静态类型引用和运行时反射发现的混合注册策略 - 生成服务注册代码并添加调试日志记录功能 - 实现精确的运行时类型引用描述和泛型类型处理
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
✅ Files skipped from review due to trivial changes (1)
🚧 Files skipped from review as they are similar to previous changes (3)
📜 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)
🧰 Additional context used📓 Path-based instructions (3)**/*.cs📄 CodeRabbit inference engine (CLAUDE.md)
Files:
**/*.{cs,csproj}📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*SourceGenerators/**/*.cs📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (5)📓 Common learnings📚 Learning: 2026-04-14T01:59:40.041ZApplied to files:
📚 Learning: 2026-04-14T01:59:40.041ZApplied to files:
📚 Learning: 2026-04-14T01:59:40.041ZApplied to files:
📚 Learning: 2026-04-06T12:45:43.921ZApplied to files:
🔇 Additional comments (9)
📝 WalkthroughWalkthrough重构 CQRS 调度器的缓存为按响应类型作用域的嵌套泛型缓存;更新相关单元测试以清理并验证按响应类型的委托缓存;并改进处理程序注册源生成器以基于 Roslyn Compilation 做可访问性检查与运行时接口发现,新增测试/辅助构建器支持 MetadataReference。 Changes
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>
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 分钟 Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 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可能会对未知类型种类(如IFunctionPointerTypeSymbol、IDynamicTypeSymbol等)过于宽松。建议在
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
📒 Files selected for processing (6)
GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.csGFramework.Cqrs/Internal/CqrsDispatcher.csGFramework.SourceGenerators.Tests/Core/GeneratorTest.csGFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.csGFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.csGFramework.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 requiredusingexplicitly in C# files
Write null-safe code that respects nullable annotations instead of suppressing warnings by default
Use the namespace patternGFramework.{Module}.{Feature}with PascalCase segments
Follow standard C# naming: Types, methods, properties, events, constants use PascalCase; interfaces useIprefix; parameters and locals use camelCase; private fields use_camelCase
Use 4 spaces for indentation (not tabs), use Allman braces, and keepusingdirectives at the top of the file sorted consistently
Prefer one primary type per file unless the surrounding project alread...
Files:
GFramework.Cqrs/Internal/CqrsDispatcher.csGFramework.SourceGenerators.Tests/Core/GeneratorTest.csGFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.csGFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.csGFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.csGFramework.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.csGFramework.SourceGenerators.Tests/Core/GeneratorTest.csGFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.csGFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.csGFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.csGFramework.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.csGFramework.SourceGenerators.Tests/Core/GeneratorTest.csGFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.csGFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.csGFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.csGFramework.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.csGFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.csGFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.csGFramework.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.csGFramework.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.csGFramework.SourceGenerators.Tests/Core/MetadataReferenceTestBuilder.csGFramework.SourceGenerators/Cqrs/CqrsHandlerRegistryGenerator.csGFramework.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.csGFramework.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.csGFramework.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: 调用助手方法签名更新正确。
InvokeRequestHandlerAsync和InvokeRequestPipelineAsync现在直接返回ValueTask<TResponse>,与上层委托类型定义保持一致,避免了不必要的类型转换。GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs (4)
108-136: 新测试有效验证了按响应类型分层缓存的行为,LGTM。
Dispatcher_Should_Cache_Request_Invokers_Per_Response_Type测试清晰地验证了:
IRequest<int>和IRequest<string>各自拥有独立的缓存条目- 重复分发不会导致缓存条目增长
测试逻辑完整,断言明确。
170-194: 反射辅助方法实现正确。
GetGenericCacheField正确处理了嵌套泛型类型的反射访问流程:查找嵌套类型 → 构造闭合泛型类型 → 获取静态字段。断言信息清晰,便于调试。
236-312: 新增测试类型定义完整。
DispatcherStringCacheRequest和DispatcherStringCacheRequestHandler为验证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方法实现完整,确保了混合注册场景的稳定排序。方法逻辑清晰:
- 收集所有注册类型(Direct、ReflectedImplementation、PreciseReflected)
- 按
HandlerInterfaceLogName排序确保确定性输出- 根据
RequiresRuntimeInterfaceDiscovery决定是否追踪已知服务类型- 最后调用
RegisterRemainingReflectedHandlerInterfaces处理剩余接口
1037-1042: 新增OrderedRegistrationKind枚举清晰地区分了注册类型。枚举设计简洁,三种类型覆盖了所有注册场景。
1084-1092:ImplementationRegistrationSpec记录新增字段设计合理。
RequiresRuntimeInterfaceDiscovery字段的添加与HandlerCandidateAnalysis保持一致,确保了数据在分析和生成阶段的正确传递。GFramework.SourceGenerators.Tests/Cqrs/CqrsHandlerRegistryGeneratorTests.cs (3)
168-254: 新增的预期输出常量正确覆盖了混合注册场景。
MixedDirectAndPreciseRegistrationsExpected和MixedReflectedImplementationAndPreciseRegistrationsExpected两个常量清晰地展示了:
- 直接注册与精确重建注册的混合输出
- 隐藏实现类型的反射解析
- Handler 接口按名称稳定排序
671-827: 混合注册场景的测试覆盖完整。两个测试方法分别验证了:
Generates_Mixed_Direct_And_Precise_Registrations_For_Same_Implementation:可见实现 + 混合接口Generates_Mixed_Reflected_Implementation_And_Precise_Registrations_For_Same_Implementation:隐藏实现 + 混合接口测试数据设计合理,覆盖了生成器的关键行为边界。
829-956: 外部 protected 类型的部分运行时接口发现测试设计良好。测试通过多程序集场景验证了:
- 生成器正确处理继承自外部程序集的 handler
- 可访问接口被直接注册
- 不可访问的 protected 类型接口回退到运行时发现
knownServiceTypes集合正确追踪已注册接口
Assert.Multiple的使用确保了所有断言都会执行,便于问题诊断。
- 实现CqrsHandlerRegistryGenerator源代码生成器 - 减少运行时程序集反射扫描开销 - 支持IRequestHandler、INotificationHandler和IStreamRequestHandler接口 - 生成静态注册代码替代运行时动态发现 - 提供精确的运行时类型引用描述功能 - 实现泛型类型和数组类型的反射处理 - 添加日志记录和错误处理机制 - 支持跨程序集类型引用和内部类型反射查找 - 生成符合IServiceCollection的服务注册代码
- 验证相同消息类型重复分发时不会重复扩张服务类型与调用委托缓存 - 验证 request 调用委托会按响应类型分别缓存避免不同响应类型共用 object 结果桥接 - 实现通过反射读取 dispatcher 静态缓存字典的测试辅助方法 - 添加清空 dispatcher 静态缓存的方法避免跨用例共享进程级状态 - 创建多个测试数据模型和处理器用于验证不同的缓存场景 - 实现异步流消费方法确保建流路径被真实执行
- 创建 MetadataReferenceTestBuilder 工具类用于构建内存元数据引用 - 实现 CreateFromSource 方法将源码编译为内存程序集并返回元数据引用 - 添加 GetRuntimeMetadataReferences 方法获取当前运行时可信平台程序集引用 - 创建 CqrsHandlerRegistryGeneratorTests 测试类验证 CQRS 处理器注册生成器功能 - 添加多种测试用例验证不同场景下的处理器注册行为 - 包含嵌套处理器、隐藏实现、数组类型参数、泛型类型定义等边界情况测试 - 实现混合直接注册和精确重建注册的测试验证 - 添加对外部基类保护类型处理器的支持测试 - 验证生成器优先处理隐藏处理器而不输出遗留回退标记的功能
- 将 Microsoft.CodeAnalysis.CSharp 引用添加到 GlobalUsings.cs - 重新排列引用顺序以符合代码风格规范
e96623b
into
refactor/cqrs-architecture-decoupling
Summary by CodeRabbit
发布说明
重构
测试