Skip to content

Feat/cqrs optimization#344

Merged
GeWuYou merged 5 commits into
mainfrom
feat/cqrs-optimization
May 9, 2026
Merged

Feat/cqrs optimization#344
GeWuYou merged 5 commits into
mainfrom
feat/cqrs-optimization

Conversation

@GeWuYou
Copy link
Copy Markdown
Owner

@GeWuYou GeWuYou commented May 9, 2026

Summary by CodeRabbit

发布说明

  • Bug 修复

    • 改进架构启动时对已配置自定义通知发布器的复用,修正重复或未复用情形。
  • 文档

    • 明确区分两种通知发布器注册方式(复用现成实例 vs 容器托管泛型注册),并新增示例说明。
  • 测试

    • 增强对通知发布器注册/重用、以及调度器实例级缓存行为的回归测试覆盖。

GeWuYou added 4 commits May 9, 2026 08:26
- 补充 UseNotificationPublisher<TPublisher>() 的单例解析与重复注册回归覆盖

- 更新 notification publisher 组合根文档,说明实例重载与泛型重载的生命周期边界

- 更新 cqrs-rewrite 跟踪与追踪文档,记录 RP-119 的验证结果与下一恢复点
- 修复默认 CQRS runtime 在工厂层过早固化顺序 publisher 的问题

- 更新 dispatcher 与基础设施接线,确保组合根注册的 publisher 能在标准 publish 路径生效

- 补充 notification publisher 回归并更新 cqrs-rewrite 的 RP-120 恢复点
- 新增标准 Architecture 启动路径下自定义 notification publisher 的集成回归,验证 Configurator 注册的策略会被真实 publish 路径复用

- 更新 cqrs-rewrite active tracking 与 trace,推进恢复点到 RP-121 并记录当前验证结果与下一步边界
- 新增 dispatcher 实例级 request behavior presence cache,减少零管道请求 steady-state 的容器查询开销

- 补充 dispatcher cache 回归并更新 cqrs-rewrite active tracking 与 trace,记录 request benchmark 和 lifetime benchmark 结果
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 9, 2026

Review Change Stack
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: 76d5ad13-7268-4901-9f7d-12f8b538c903

📥 Commits

Reviewing files that changed from the base of the PR and between 56dc4fd and 17e7f64.

📒 Files selected for processing (7)
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
  • ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
✅ Files skipped from review due to trivial changes (2)
  • ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
  • ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.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). (3)
  • GitHub Check: Build and Test
  • GitHub Check: Code Quality & Security
  • GitHub Check: Analyze (C#)
🧰 Additional context used
📓 Path-based instructions (8)
**/Cqrs/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

Use CQRS (Command Query Responsibility Segregation) pattern with the Cqrs naming entry point instead of the historical Mediator alias

Files:

  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Apply [Log] attribute for automatic logging field and logging helper method generation
Apply [Priority] attribute for automatic priority comparison implementation generation
Apply [GenerateEnumExtensions] attribute to generate enumeration extension capabilities
Apply [ContextAware] attribute to automatically implement IContextAware boilerplate logic

**/*.cs: All public, protected, and internal types and members MUST include XML documentation comments (///) in C#
XML documentation MUST use <summary>, <param>, <returns>, <exception>, and <remarks> where applicable, and explain intent, contract, and usage constraints instead of restating syntax
If a C# member participates in lifecycle, threading, registration, or disposal behavior, document that behavior explicitly
Core framework components (Architecture, Module, System, Context, Registry, Service Module, Lifecycle types) MUST include high-level explanations of responsibilities, lifecycle, interaction with other components, why abstraction exists, and when to use instead of alternatives
Generated logic and source generator pipelines MUST explain what is generated, why it is generated, semantic assumptions the generator relies on, and any diagnostics or fallback behavior
Do not rely on implicit imports. Declare every required using explicitly in C#
Write null-safe code that respects nullable annotations instead of suppressing warnings by default in C#
Use namespace pattern GFramework.{Module}.{Feature} with PascalCase segments in C#
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 Allman braces style for C#
Keep using directives at the top of the file and sort them consistently in C#
Prefer one primary type per file unless surrounding project already uses different local pattern
Prefer explicit, readable code over clever shorthand in framework internals
M...

Files:

  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
**/*[!.]*

📄 CodeRabbit inference engine (AGENTS.md)

For files with shebang lines, keep shebang as first line and place license header immediately after it

Files:

  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
**/*.{cs,ts,tsx,js,jsx,py,sh}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{cs,ts,tsx,js,jsx,py,sh}: All generated or modified code MUST include clear and meaningful comments where required by documentation rules
Comments MUST NOT be trivial, redundant, or misleading. Prefer explaining why and when, not just what. Code should remain understandable without requiring external context
Avoid obvious comments such as // increment i

Files:

  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
**/*.{cs,ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{cs,ts,tsx,js,jsx,py}: Add inline comments for non-trivial logic, concurrency/threading behavior, performance-sensitive paths, workarounds/compatibility constraints/edge cases, and registration order/lifecycle sequencing/generated code assumptions
Methods with non-trivial logic MUST document core idea, key decisions, and edge case handling
Separate logical blocks with blank lines when it improves readability
Unless there is clear and documented reason to keep file large, keep single source file under roughly 800-1000 lines
Validate external or user-controlled input before it reaches file system, serialization, reflection, code generation, or process boundaries
Do not build command strings, file paths, type names, or generated code from untrusted input without strict validation or allow-listing
Avoid logging secrets, tokens, credentials, or machine-specific sensitive data
Prefer least-privilege behavior for file, process, and environment access

Files:

  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
**/*.{csproj,cs}

📄 CodeRabbit inference engine (AGENTS.md)

Framework runtime, abstractions, and meta-package projects MUST NOT reference *.SourceGenerators* projects or packages, and MUST NOT use source-generator attributes

Files:

  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
**/*.{cs,ts,tsx,js,jsx,py,sh,xml,csproj,props,targets}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation. Do not use tabs

Files:

  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
**/*.{cs,ts,tsx,js,jsx,py,sh,xml}

📄 CodeRabbit inference engine (AGENTS.md)

Keep line length readable. Around 120 characters is preferred upper bound

Files:

  • GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
🧠 Learnings (1)
📚 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.Tests/Cqrs/CqrsNotificationPublisherTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs
🔇 Additional comments (3)
GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs (2)

10-10: 正确补充了必要的 using 引用。

新增的 using GFramework.Cqrs.Notification; 为后续的 INotificationPublisher 类型引用提供了命名空间支持,符合显式声明依赖的编码规范。


188-191: 正确适配了通知发布器的延迟解析语义。

此 mock 配置反映了 PR 的核心变更:PublishAsync 现在会在发布时延迟查询容器中的 INotificationPublisher 注册,而非在工厂创建阶段预解析。对于 MockBehavior.Strict 模式,显式返回空集合可避免 mock 失败,同时触发运行时的默认回退路径。注释清晰解释了配置意图,且放置在共享的 CreateRuntime 辅助方法中可统一应用于所有测试。

GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs (1)

186-189: 延迟解析的 mock 配置正确且一致。

此配置与 CqrsDispatcherContextValidationTests.cs 保持一致,为严格 mock 模式下的延迟发布器解析提供了必要的空注册默认行为。注释准确说明了配置的双重目的:支持运行时的延迟解析语义,同时满足严格 mock 的显式契约要求。各测试可根据需要通过 notificationPublisher 参数或容器配置覆盖此默认值。


📝 Walkthrough

Walkthrough

PR 在 CqrsDispatcher 中引入实例级请求行为存在性缓存,支持通知发布器的延迟容器解析;工厂与运行时改为不预解析发布器;新增综合测试覆盖缓存隔离性与发布器接线;同步更新文档与迁移追踪。

Changes

CQRS 请求行为缓存与通知发布器延迟解析

Layer / File(s) Summary
Dispatcher 核心实现
GFramework.Cqrs/Internal/CqrsDispatcher.cs
添加 _requestBehaviorPresenceCache 缓存请求行为注册状态;构造函数改为接受可空发布器;PublishAsync 改为通过 ResolveNotificationPublisher() 延迟解析;SendAsync 改为调用 HasRequestBehaviorRegistration() 缓存查询。
工厂与运行时集成
GFramework.Cqrs/CqrsRuntimeFactory.cs, GFramework.Core/Services/Modules/CqrsRuntimeModule.cs, GFramework.Tests.Common/CqrsTestRuntime.cs
CqrsRuntimeFactory 文档明确发布器解析优先级;CqrsRuntimeModule 与 CqrsTestRuntime 改为调用两参数工厂方法,移除预解析逻辑。
请求行为缓存测试
GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
新增主测试验证缓存按 dispatcher 实例隔离;添加反射工具获取 dispatcher 实例、克隆容器与检查私有缓存。
通知发布器注册测试
GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
扩展现有策略测试的冻结后断言;新增泛型重载注册、集成执行与重复声明拒绝测试;补充 NotificationPublisherProbe 与 DependencyAwareNotificationPublisher 辅助类。
架构集成测试
GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
新增初始化测试验证架构启动路径复用自定义发布器;添加状态初始化/清理;引入 ConfiguredNotificationPublisherArchitecture 与相关测试桩。
文档与示例
GFramework.Cqrs/README.md, docs/zh-CN/core/cqrs.md
细化通知发布器策略说明与泛型重载示例。
迁移追踪
ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md, ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
更新恢复点至 RP-122/PR #344;记录实现、测试、基准与验证结果;补齐阶段追踪文档。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • GeWuYou/GFramework#340: 同样修改 CqrsDispatcher 请求行为快路径,增加 HasRegistration 优化与无行为时的 pipeline 绕过。
  • GeWuYou/GFramework#342: 修改 CQRS 通知发布器的实现与注册 API,容器解析语义直接相关。
  • GeWuYou/GFramework#341: 同样修改 CqrsDispatcher 与运行时工厂,添加请求行为缓存与延迟发布器解析的相关测试。
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive 标题「Feat/cqrs optimization」过于宽泛,未能清晰传达本次变更的核心内容。虽然涉及CQRS相关优化,但此标题无法区分是否为性能优化、功能新增、还是缺陷修复。 建议使用更具体的标题,如「Fix/Optimize CQRS notification publisher resolution and dispatcher behavior cache」,以准确反映通知发布器解析修复、架构启动路径集成和调度器缓存优化的核心变更。
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 feat/cqrs-optimization

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 9, 2026

Greptile Summary

This PR fixes a bug where custom INotificationPublisher instances registered via Architecture.Configurator were silently ignored, because CqrsRuntimeModule.Register() attempted to resolve the publisher from the container before the container was frozen (and thus before the DI provider had materialized the registration). The fix defers publisher resolution to the first PublishAsync call, at which point the container is frozen and all registrations are available.

  • CqrsRuntimeModule.cs: Removes the premature container.Get<INotificationPublisher>() call at module registration time; CqrsRuntimeFactory.CreateRuntime is now called without an explicit publisher, delegating resolution to the dispatcher.
  • CqrsDispatcher.cs: Adds an instance-level _resolvedNotificationPublisher field populated on first publish via Interlocked.CompareExchange, avoiding repeated container lookups and default-publisher allocations on the hot path.
  • Tests: New test InitializeAsync_Should_Reuse_Custom_NotificationPublisher_From_Configurator and several new unit tests in CqrsNotificationPublisherTests and NotificationPublisherRegistrationExtensionsTests directly cover the fixed scenario and the lazy-resolution semantics.

Confidence Score: 5/5

Safe to merge — the change is a targeted, well-tested bug fix with no side effects on existing callers.

The fix is minimal and surgical: one line removed from CqrsRuntimeModule.Register() and a lazy-initialisation field added to CqrsDispatcher. All changed call sites use the same container already passed into the dispatcher, so there are no new dependencies or lifecycle mismatches. The deferred resolution path is covered by new integration tests exercising the full architecture initialisation flow, and the hot-path caching uses a standard Interlocked.CompareExchange pattern with a benign worst-case (two threads race to initialise; the loser's instance is discarded and the winner's is cached).

No files require special attention.

Important Files Changed

Filename Overview
GFramework.Core/Services/Modules/CqrsRuntimeModule.cs Removes premature container.Get() at registration time, fixing the root cause where custom publishers were invisible before container freeze.
GFramework.Cqrs/Internal/CqrsDispatcher.cs Adds _resolvedNotificationPublisher instance field with Interlocked.CompareExchange-based lazy initialisation in ResolveNotificationPublisher(); correctly handles explicit-instance, container-resolved, and default-fallback paths without per-call allocation.
GFramework.Cqrs/CqrsRuntimeFactory.cs Adds a no-publisher overload of CreateRuntime that delegates to the existing three-parameter overload with null, enabling callers to opt into deferred publisher resolution without ceremony.
GFramework.Tests.Common/CqrsTestRuntime.cs RegisterInfrastructure now calls the new no-publisher overload of CreateRuntime, keeping test infrastructure consistent with the production module path and allowing pre-registered publishers to be picked up at first publish.
GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs New and updated tests cover explicit-publisher, container-resolved-publisher, and default-fallback paths; the strict mock setup for GetAll(INotificationPublisher) correctly mirrors the deferred resolution contract.
GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs New tests validate instance-registration, generic-type-registration, duplicate-registration guard, and end-to-end infrastructure reuse for both TaskWhenAll and Sequential publishers.
GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs New integration test exercises the full architecture initialisation path with a custom publisher registered via Configurator, directly proving the CqrsRuntimeModule bug is resolved end-to-end.
GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs Cache-test scaffolding updated with new state-reset helpers for the notification context; no logic concerns.

Sequence Diagram

sequenceDiagram
    participant Cfg as Architecture.Configurator
    participant Mod as CqrsRuntimeModule.Register()
    participant Dis as CqrsDispatcher
    participant Con as IIocContainer (frozen)

    Note over Cfg,Con: Architecture initialization
    Cfg->>Con: Register(INotificationPublisher, CustomPublisher)
    Mod->>Dis: new CqrsDispatcher(container, logger, null)
    Note right of Dis: _notificationPublisher = null<br/>_resolvedNotificationPublisher = null
    Mod->>Con: container.Freeze()

    Note over Dis,Con: First PublishAsync call
    Dis->>Dis: ResolveNotificationPublisher()
    alt "_notificationPublisher != null"
        Dis-->>Dis: return _notificationPublisher (explicit instance)
    else "_resolvedNotificationPublisher != null"
        Dis-->>Dis: return cached publisher (fast path)
    else first call
        Dis->>Con: container.GetAll(typeof(INotificationPublisher))
        Con-->>Dis: [CustomPublisher]
        Dis->>Dis: Interlocked.CompareExchange(ref _resolvedNotificationPublisher, CustomPublisher, null)
        Dis-->>Dis: return _resolvedNotificationPublisher (CustomPublisher)
    end
    Dis->>Dis: publisher.PublishAsync(publishContext, cancellationToken)
Loading

Reviews (2): Last reviewed commit: "fix(cqrs): 收口PR344通知发布评审问题" | Re-trigger Greptile

Comment thread GFramework.Cqrs/Internal/CqrsDispatcher.cs
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

Summary

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Other ❓ Flaky 🍂 Duration ⏱️
2341    ↑11 2339    ↑9 2    ↑2 0 0 0 38.5s    ↓373ms

Test Results

failed 2 failed ↑2
passed 2339 passed ↑9

Details

tests 2341 tests ↑11
clock 38.5s ↓373ms
tool nunit
build CI - Build & Test arrow-right build-and-test link #1098
pull-request Feat/cqrs optimization link #344

Failed Tests

CqrsNotificationPublisherTests arrow-right PublishAsync_Should_Stop_After_First_Handler_Exception_When_Using_Default_Publisher
CqrsDispatcherContextValidationTests arrow-right PublishAsync_Should_Throw_When_Context_Does_Not_Implement_IArchitectureContext

Insights

Average Tests per Run Total Flaky Tests Total Failed Slowest Test (p95)
2241 0 3 4.7s

Fail Rate

Fail Rate 0.00%
Test 📝 Results 📊 Passed ✅ Failed ❌ Fail Rate (%) 📈
CreateStream_Should_Throw_When_Stream_Pipeline_Behavior_Context_Does_Not_Implement_IArchitectureContext 10 9 1 10.00    ↓1.11
PublishAsync_Should_Stop_After_First_Handler_Exception_When_Using_Default_Publisher 28 27 1 3.57    ↑3.57
PublishAsync_Should_Throw_When_Context_Does_Not_Implement_IArchitectureContext 32 31 1 3.13    ↑3.13

build-and-test: Run #1098

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
2341 2339 2 0 0 0 0 38.5s

Some tests failed!

Name Failure Message
❌ PublishAsync_Should_Stop_After_First_Handler_Exception_When_Using_Default_Publisher Assert.That(async () => await runtime.PublishAsync(new FakeCqrsContext(), new PublisherNotification()).ConfigureAwait(false), Throws.InvalidOperationException.With.Message.EqualTo("boom")) Expected: <System.InvalidOperationException> and property Message equal to "boom" But was: <Moq.MockException: IIocContainer.GetAll(GFramework.Cqrs.Notification.INotificationPublisher) invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup. at Moq.FailForStrictMock.Handle(Invocation invocation, Mock mock) in /_/src/Moq/Interception/InterceptionAspects.cs:line 182 at Moq.Mock.Moq.IInterceptor.Intercept(Invocation invocation) in /_/src/Moq/Interception/Mock.cs:line 27 at Moq.CastleProxyFactory.Interceptor.Intercept(IInvocation underlying) in /_/src/Moq/Interception/CastleProxyFactory.cs:line 107 at Castle.DynamicProxy.AbstractInvocation.Proceed() at Castle.Proxies.IIocContainerProxy.GetAll(Type type) at GFramework.Cqrs.Internal.CqrsDispatcher.ResolveNotificationPublisher() in /home/runner/work/GFramework/GFramework/GFramework.Cqrs/Internal/CqrsDispatcher.cs:line 242 at GFramework.Cqrs.Internal.CqrsDispatcher.PublishAsync[TNotification](ICqrsContext context, TNotification notification, CancellationToken cancellationToken) in /home/runner/work/GFramework/GFramework/GFramework.Cqrs/Internal/CqrsDispatcher.cs:line 103 at GFramework.Cqrs.Tests.Cqrs.CqrsNotificationPublisherTests.<>c__DisplayClass5_0.<<PublishAsync_Should_Stop_After_First_Handler_Exception_When_Using_Default_Publisher>b__1>d.MoveNext() in /home/runner/work/GFramework/GFramework/GFramework.Cqrs.Tests/Cqrs/CqrsNotificationPublisherTests.cs:line 168 --- End of stack trace from previous location --- at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.GetResult() at NUnit.Framework.Internal.AsyncToSyncAdapter.Await[TResult](TestExecutionContext context, Func`1 invoke) at NUnit.Framework.Internal.AsyncToSyncAdapter.Await(TestExecutionContext context, Func`1 invoke) at NUnit.Framework.Internal.ExceptionHelper.RecordException(Delegate parameterlessDelegate, String parameterName)>
❌ PublishAsync_Should_Throw_When_Context_Does_Not_Implement_IArchitectureContext Assert.That(async () => await runtime.PublishAsync(new FakeCqrsContext(), new ContextAwareNotification()).ConfigureAwait(false), Throws.InvalidOperationException.With.Message.Contains("does not implement IArchitectureContext")) Expected: <System.InvalidOperationException> and property Message containing "does not implement IArchitectureContext" But was: <Moq.MockException: IIocContainer.GetAll(GFramework.Cqrs.Notification.INotificationPublisher) invocation failed with mock behavior Strict. All invocations on the mock must have a corresponding setup. at Moq.FailForStrictMock.Handle(Invocation invocation, Mock mock) in /_/src/Moq/Interception/InterceptionAspects.cs:line 182 at Moq.Mock.Moq.IInterceptor.Intercept(Invocation invocation) in /_/src/Moq/Interception/Mock.cs:line 27 at Moq.CastleProxyFactory.Interceptor.Intercept(IInvocation underlying) in /_/src/Moq/Interception/CastleProxyFactory.cs:line 107 at Castle.DynamicProxy.AbstractInvocation.Proceed() at Castle.Proxies.IIocContainerProxy.GetAll(Type type) at GFramework.Cqrs.Internal.CqrsDispatcher.ResolveNotificationPublisher() in /home/runner/work/GFramework/GFramework/GFramework.Cqrs/Internal/CqrsDispatcher.cs:line 242 at GFramework.Cqrs.Internal.CqrsDispatcher.PublishAsync[TNotification](ICqrsContext context, TNotification notification, CancellationToken cancellationToken) in /home/runner/work/GFramework/GFramework/GFramework.Cqrs/Internal/CqrsDispatcher.cs:line 103 at GFramework.Cqrs.Tests.Cqrs.CqrsDispatcherContextValidationTests.<>c__DisplayClass3_0.<<PublishAsync_Should_Throw_When_Context_Does_Not_Implement_IArchitectureContext>b__1>d.MoveNext() in /home/runner/work/GFramework/GFramework/GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherContextValidationTests.cs:line 119 --- End of stack trace from previous location --- at NUnit.Framework.Internal.TaskAwaitAdapter.GenericAdapter`1.GetResult() at NUnit.Framework.Internal.AsyncToSyncAdapter.Await[TResult](TestExecutionContext context, Func`1 invoke) at NUnit.Framework.Internal.AsyncToSyncAdapter.Await(TestExecutionContext context, Func`1 invoke) at NUnit.Framework.Internal.ExceptionHelper.RecordException(Delegate parameterlessDelegate, String parameterName)>

Slowest Tests

Test 📝 Results 📊 Duration (avg) ⏱️ Duration (p95) ⏱️
CreateStream_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 28 4.1s 4.7s
SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 43 1.4s 4.7s
Does_Not_Report_When_FieldInjectedModel_Is_Registered 43 2.2s 2.3s
Generates_Scene_Behavior_Boilerplate 43 1.9s 2.0s
CleanupDuringAcquire_Should_NotCauseRaceCondition 43 1.1s 1.1s
Append_ShouldNotBlock 43 1.0s 1.0s
Context_Caching_Should_Improve_Performance 43 782ms 794ms
PendingCount_ShouldReflectQueuedEntries 43 501ms 501ms
Cleanup_Should_NotRemoveActiveLocks 43 404ms 405ms
Cleanup_Should_RemoveUnusedLocks 43 401ms 402ms

± Comparison with run #1095 at 571c677 | 🍂 No flaky tests detected across all runs. | ⏱️ Measured over 43 runs.

Github Test Reporter by CTRF 💚

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

⚠️MegaLinter analysis: Success with warnings

Descriptor Linter Files Fixed Errors Warnings Elapsed time
⚠️ CSHARP dotnet-format yes 1 no 5.37s
✅ REPOSITORY gitleaks yes no no 8.72s
✅ REPOSITORY trufflehog yes no no 6.56s

Detailed Issues

⚠️ CSHARP / dotnet-format - 1 error
Welcome to .NET 9.0!
---------------------
SDK Version: 9.0.114

----------------
Installed an ASP.NET Core HTTPS development certificate.
To trust the certificate, run 'dotnet dev-certs https --trust'
Learn about HTTPS: https://aka.ms/dotnet-https

----------------
Write your first app: https://aka.ms/dotnet-hello-world
Find out what's new: https://aka.ms/dotnet-whats-new
Explore documentation: https://aka.ms/dotnet-docs
Report issues and find source on GitHub: https://github.com/dotnet/core
Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
Unhandled exception: System.Exception: Restore operation failed.
   at Microsoft.CodeAnalysis.Tools.CodeFormatter.OpenMSBuildWorkspaceAsync(String solutionOrProjectPath, WorkspaceType workspaceType, Boolean noRestore, Boolean requiresSemantics, String binaryLogPath, Boolean logWorkspaceWarnings, ILogger logger, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Tools.CodeFormatter.FormatWorkspaceAsync(FormatOptions formatOptions, ILogger logger, CancellationToken cancellationToken, String binaryLogPath)
   at Microsoft.CodeAnalysis.Tools.FormatCommandCommon.FormatAsync(FormatOptions formatOptions, ILogger`1 logger, CancellationToken cancellationToken)
   at Microsoft.CodeAnalysis.Tools.Commands.RootFormatCommand.FormatCommandDefaultHandler.InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)
   at System.CommandLine.Invocation.InvocationPipeline.InvokeAsync(ParseResult parseResult, CancellationToken cancellationToken)

See detailed reports in MegaLinter artifacts
Set VALIDATE_ALL_CODEBASE: true in mega-linter.yml to validate all sources, not only the diff

MegaLinter is graciously provided by OX Security
Show us your support by starring ⭐ the repository

@GeWuYou
Copy link
Copy Markdown
Owner Author

GeWuYou commented May 9, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 9, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

Actionable comments posted: 2

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

645-663: ⚡ Quick win

抽出共享的容器装配逻辑,避免隔离容器与夹具配置漂移。

CreateFrozenContainer() 现在复制了 SetUp() 里的整套 CQRS 行为和 handler 注册。后续只要一边增删注册、另一边忘记同步,这个“实例级缓存隔离”回归就可能因为测试装配差异而误报或漏报。建议把这段注册提成一个共享 helper,让两个入口复用同一份容器形状。

♻️ 建议的整理方式
+    private static void ConfigureDispatcherCacheFixture(MicrosoftDiContainer container)
+    {
+        container.RegisterCqrsPipelineBehavior<DispatcherPipelineCacheBehavior>();
+        container.RegisterCqrsPipelineBehavior<DispatcherPipelineContextRefreshBehavior>();
+        container.RegisterCqrsPipelineBehavior<DispatcherPipelineOrderOuterBehavior>();
+        container.RegisterCqrsPipelineBehavior<DispatcherPipelineOrderInnerBehavior>();
+        container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineCacheBehavior>();
+        container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineContextRefreshBehavior>();
+        container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineOrderOuterBehavior>();
+        container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineOrderInnerBehavior>();
+
+        CqrsTestRuntime.RegisterHandlers(
+            container,
+            typeof(CqrsDispatcherCacheTests).Assembly,
+            typeof(ArchitectureContext).Assembly);
+    }
+
     public void SetUp()
     {
         LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
         _container = new MicrosoftDiContainer();
-        _container.RegisterCqrsPipelineBehavior<DispatcherPipelineCacheBehavior>();
-        _container.RegisterCqrsPipelineBehavior<DispatcherPipelineContextRefreshBehavior>();
-        _container.RegisterCqrsPipelineBehavior<DispatcherPipelineOrderOuterBehavior>();
-        _container.RegisterCqrsPipelineBehavior<DispatcherPipelineOrderInnerBehavior>();
-        _container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineCacheBehavior>();
-        _container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineContextRefreshBehavior>();
-        _container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineOrderOuterBehavior>();
-        _container.RegisterCqrsStreamPipelineBehavior<DispatcherStreamPipelineOrderInnerBehavior>();
-
-        CqrsTestRuntime.RegisterHandlers(
-            _container,
-            typeof(CqrsDispatcherCacheTests).Assembly,
-            typeof(ArchitectureContext).Assembly);
+        ConfigureDispatcherCacheFixture(_container);
         _container.Freeze();
         _context = new ArchitectureContext(_container);
         ...
     }
 
     private static MicrosoftDiContainer CreateFrozenContainer()
     {
         var container = new MicrosoftDiContainer();
-        container.RegisterCqrsPipelineBehavior<DispatcherPipelineCacheBehavior>();
-        ...
-        CqrsTestRuntime.RegisterHandlers(
-            container,
-            typeof(CqrsDispatcherCacheTests).Assembly,
-            typeof(ArchitectureContext).Assembly);
+        ConfigureDispatcherCacheFixture(container);
         container.Freeze();
         return container;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs` around lines 645 -
663, Create a single shared helper that builds the CQRS container assembly used
by both CreateFrozenContainer() and the test SetUp() to avoid drift: move the
repeated RegisterCqrsPipelineBehavior/ RegisterCqrsStreamPipelineBehavior calls
and the CqrsTestRuntime.RegisterHandlers(...) invocation into a new method
(e.g., BuildCqrsContainerShape or ConfigureCqrsContainer) that returns or
mutates a MicrosoftDiContainer, then call that helper from
CreateFrozenContainer() and SetUp() before calling container.Freeze() so both
places reuse the exact same registration shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs`:
- Around line 109-113: Add an assertion that INotificationPublisher is
registered exactly once: after the existing checks (returnedContainer,
container.HasRegistration, and GetRequired assertions) assert that the container
contains exactly one registration for the INotificationPublisher service (for
example by verifying container.GetAll<INotificationPublisher>().Count() == 1 or
filtering container.Registrations by service type INotificationPublisher and
asserting Count == 1), while keeping the existing check that the resolved
instance is of type TrackingNotificationPublisher and resolves to the same
singleton instance.

In `@GFramework.Cqrs/Internal/CqrsDispatcher.cs`:
- Around line 242-249: Tests using a strict IIocContainer mock are failing
because ResolveNotificationPublisher() calls
container.GetAll(typeof(INotificationPublisher)) but the mock wasn't configured;
update the mocks in CqrsDispatcherContextValidationTests.cs and
CqrsNotificationPublisherTests.cs to setup
IIocContainer.GetAll(typeof(INotificationPublisher)) to return an empty
collection so ResolveNotificationPublisher() can fall back to
SequentialNotificationPublisher; target the IIocContainer mock setup for GetAll
to ensure PublishAsync calls don't throw.

---

Nitpick comments:
In `@GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs`:
- Around line 645-663: Create a single shared helper that builds the CQRS
container assembly used by both CreateFrozenContainer() and the test SetUp() to
avoid drift: move the repeated RegisterCqrsPipelineBehavior/
RegisterCqrsStreamPipelineBehavior calls and the
CqrsTestRuntime.RegisterHandlers(...) invocation into a new method (e.g.,
BuildCqrsContainerShape or ConfigureCqrsContainer) that returns or mutates a
MicrosoftDiContainer, then call that helper from CreateFrozenContainer() and
SetUp() before calling container.Freeze() so both places reuse the exact same
registration shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1f718ae6-fad8-4b26-992f-d908ff7ceb98

📥 Commits

Reviewing files that changed from the base of the PR and between 3fbc563 and 56dc4fd.

📒 Files selected for processing (11)
  • GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
  • GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • GFramework.Cqrs/CqrsRuntimeFactory.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.Cqrs/README.md
  • GFramework.Tests.Common/CqrsTestRuntime.cs
  • ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
  • ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
  • docs/zh-CN/core/cqrs.md
📜 Review details
🧰 Additional context used
📓 Path-based instructions (10)
**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.cs: Apply [Log] attribute for automatic logging field and logging helper method generation
Apply [Priority] attribute for automatic priority comparison implementation generation
Apply [GenerateEnumExtensions] attribute to generate enumeration extension capabilities
Apply [ContextAware] attribute to automatically implement IContextAware boilerplate logic

**/*.cs: All public, protected, and internal types and members MUST include XML documentation comments (///) in C#
XML documentation MUST use <summary>, <param>, <returns>, <exception>, and <remarks> where applicable, and explain intent, contract, and usage constraints instead of restating syntax
If a C# member participates in lifecycle, threading, registration, or disposal behavior, document that behavior explicitly
Core framework components (Architecture, Module, System, Context, Registry, Service Module, Lifecycle types) MUST include high-level explanations of responsibilities, lifecycle, interaction with other components, why abstraction exists, and when to use instead of alternatives
Generated logic and source generator pipelines MUST explain what is generated, why it is generated, semantic assumptions the generator relies on, and any diagnostics or fallback behavior
Do not rely on implicit imports. Declare every required using explicitly in C#
Write null-safe code that respects nullable annotations instead of suppressing warnings by default in C#
Use namespace pattern GFramework.{Module}.{Feature} with PascalCase segments in C#
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 Allman braces style for C#
Keep using directives at the top of the file and sort them consistently in C#
Prefer one primary type per file unless surrounding project already uses different local pattern
Prefer explicit, readable code over clever shorthand in framework internals
M...

Files:

  • GFramework.Cqrs/CqrsRuntimeFactory.cs
  • GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.Tests.Common/CqrsTestRuntime.cs
  • GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
**/*[!.]*

📄 CodeRabbit inference engine (AGENTS.md)

For files with shebang lines, keep shebang as first line and place license header immediately after it

Files:

  • GFramework.Cqrs/CqrsRuntimeFactory.cs
  • GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.Tests.Common/CqrsTestRuntime.cs
  • GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
  • GFramework.Cqrs/README.md
  • docs/zh-CN/core/cqrs.md
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
  • ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
**/*.{cs,ts,tsx,js,jsx,py,sh}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{cs,ts,tsx,js,jsx,py,sh}: All generated or modified code MUST include clear and meaningful comments where required by documentation rules
Comments MUST NOT be trivial, redundant, or misleading. Prefer explaining why and when, not just what. Code should remain understandable without requiring external context
Avoid obvious comments such as // increment i

Files:

  • GFramework.Cqrs/CqrsRuntimeFactory.cs
  • GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.Tests.Common/CqrsTestRuntime.cs
  • GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
**/*.{cs,ts,tsx,js,jsx,py}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{cs,ts,tsx,js,jsx,py}: Add inline comments for non-trivial logic, concurrency/threading behavior, performance-sensitive paths, workarounds/compatibility constraints/edge cases, and registration order/lifecycle sequencing/generated code assumptions
Methods with non-trivial logic MUST document core idea, key decisions, and edge case handling
Separate logical blocks with blank lines when it improves readability
Unless there is clear and documented reason to keep file large, keep single source file under roughly 800-1000 lines
Validate external or user-controlled input before it reaches file system, serialization, reflection, code generation, or process boundaries
Do not build command strings, file paths, type names, or generated code from untrusted input without strict validation or allow-listing
Avoid logging secrets, tokens, credentials, or machine-specific sensitive data
Prefer least-privilege behavior for file, process, and environment access

Files:

  • GFramework.Cqrs/CqrsRuntimeFactory.cs
  • GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.Tests.Common/CqrsTestRuntime.cs
  • GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
**/*.{csproj,cs}

📄 CodeRabbit inference engine (AGENTS.md)

Framework runtime, abstractions, and meta-package projects MUST NOT reference *.SourceGenerators* projects or packages, and MUST NOT use source-generator attributes

Files:

  • GFramework.Cqrs/CqrsRuntimeFactory.cs
  • GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.Tests.Common/CqrsTestRuntime.cs
  • GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
**/*.{cs,ts,tsx,js,jsx,py,sh,xml,csproj,props,targets}

📄 CodeRabbit inference engine (AGENTS.md)

Use 4 spaces for indentation. Do not use tabs

Files:

  • GFramework.Cqrs/CqrsRuntimeFactory.cs
  • GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.Tests.Common/CqrsTestRuntime.cs
  • GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
**/*.{cs,ts,tsx,js,jsx,py,sh,xml}

📄 CodeRabbit inference engine (AGENTS.md)

Keep line length readable. Around 120 characters is preferred upper bound

Files:

  • GFramework.Cqrs/CqrsRuntimeFactory.cs
  • GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.Tests.Common/CqrsTestRuntime.cs
  • GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{md,mdx}: Keep code samples, package names, and command examples aligned with current repository state in documentation
When public page references XML docs or API coverage, convert evidence into reader-facing guidance: explain which types/namespaces/entry points readers should inspect and why
For integration-oriented features such as AI-First config system, documentation MUST cover: project directory layout/file conventions, required project/package wiring, minimal working example, migration/compatibility notes
When examples are rewritten, preserve only parts that remain true. Delete or replace speculative examples instead of lightly editing into another inaccurate form

Files:

  • GFramework.Cqrs/README.md
  • docs/zh-CN/core/cqrs.md
  • ai-plan/public/cqrs-rewrite/traces/cqrs-rewrite-migration-trace.md
  • ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md
docs/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation should be organized with Chinese content in docs/zh-CN/ and structured to include getting started, module-specific capabilities (Core, Game, Godot, ECS), source generator usage, tutorials, best practices, and troubleshooting

Files:

  • docs/zh-CN/core/cqrs.md
**/Cqrs/**/*.cs

📄 CodeRabbit inference engine (CLAUDE.md)

Use CQRS (Command Query Responsibility Segregation) pattern with the Cqrs naming entry point instead of the historical Mediator alias

Files:

  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
🧠 Learnings (1)
📚 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/CqrsRuntimeFactory.cs
  • GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
  • GFramework.Cqrs/Internal/CqrsDispatcher.cs
  • GFramework.Tests.Common/CqrsTestRuntime.cs
  • GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
  • GFramework.Cqrs.Tests/Cqrs/CqrsDispatcherCacheTests.cs
  • GFramework.Cqrs.Tests/Cqrs/NotificationPublisherRegistrationExtensionsTests.cs
🪛 GitHub Actions: CI - Build & Test / Build and Test
GFramework.Cqrs/Internal/CqrsDispatcher.cs

[error] 242-242: Strict Moq exception during ResolveNotificationPublisher(): IIocContainer.GetAll(GFramework.Cqrs.Notification.INotificationPublisher) invocation failed with mock behavior Strict.


[error] 103-103: Strict Moq exception surfaced while calling CqrsDispatcher.PublishAsync(ICqrsContext, TNotification, CancellationToken).

🔇 Additional comments (11)
GFramework.Cqrs/README.md (1)

135-135: 文档更新准确反映了运行时行为。

通知发布器策略表与示例代码正确区分了实例注册 UseNotificationPublisher(...) 与泛型注册 UseNotificationPublisher<TPublisher>() 的语义,与 CqrsDispatcher 的延迟解析逻辑和 CqrsRuntimeFactory 的文档说明保持一致。

Also applies to: 164-170

docs/zh-CN/core/cqrs.md (1)

128-128: LGTM!

中文文档与英文 README 保持同步,准确描述了通知发布器的两种注册方式及其生命周期语义。

Also applies to: 163-169

ai-plan/public/cqrs-rewrite/todos/cqrs-rewrite-migration-tracking.md (1)

10-25: LGTM!

迁移跟踪文档准确记录了 RP-122 的实现细节:dispatcher 实例级请求行为存在性缓存、benchmark 结果对比,以及验证脚本输出。文档清晰说明了缓存隔离性(按 dispatcher 实例)与性能收益。

Also applies to: 88-95, 169-191

GFramework.Cqrs/Internal/CqrsDispatcher.cs (2)

24-27: 实例级行为存在性缓存实现正确。

_requestBehaviorPresenceCache 通过 ConcurrentDictionary<Type, bool> 按 behavior 服务类型缓存首次 HasRegistration 查询结果,有效减少零管道请求在稳态路径上的容器查询开销。文档清晰说明了缓存作用域(dispatcher 实例级)与线程安全性。

Also applies to: 153-166


69-72: 延迟通知发布器解析逻辑符合预期。

ResolveNotificationPublisher() 的优先级顺序清晰:显式实例 → 容器注册的单例 → 默认顺序发布器。文档准确说明了"容器冻结前创建 runtime 时无法提前拿到底层 provider 构造的实例"这一延迟解析的必要性。

Also applies to: 227-250

GFramework.Cqrs/CqrsRuntimeFactory.cs (1)

27-30: LGTM!

工厂方法的 XML 文档准确说明了 notificationPublisher 参数为 null 时的解析优先级:优先复用容器中声明的 INotificationPublisher,否则回退到默认顺序发布器。与 CqrsDispatcher.ResolveNotificationPublisher() 的实现逻辑一致。

Also applies to: 44-47

GFramework.Core/Services/Modules/CqrsRuntimeModule.cs (1)

48-48: 运行时创建改为依赖延迟解析。

CqrsRuntimeModule.Register 现在直接调用 CreateRuntime(container, dispatcherLogger),不再提前解析 INotificationPublisher。这符合 PR 目标,将发布器选择推迟到真正 PublishAsync 时,避免在容器冻结前提前固化策略。

基于 AI summary,GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs 已新增集成测试 InitializeAsync_Should_Reuse_Custom_NotificationPublisher_From_Configurator() 验证标准启动路径复用自定义发布器。

GFramework.Tests.Common/CqrsTestRuntime.cs (1)

67-67: LGTM!

测试运行时创建路径与生产代码保持一致,同样改为延迟解析通知发布器。这确保测试环境能够真实反映生产行为。

GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs (3)

164-191: 新集成测试验证自定义发布器复用路径。

InitializeAsync_Should_Reuse_Custom_NotificationPublisher_From_Configurator() 通过以下机制验证标准架构启动路径会复用 Configurator 中声明的自定义 INotificationPublisher

  1. 使用 Moq 创建包含 CQRS registry 元数据的模拟程序集
  2. 注册 ArchitectureTrackingNotificationPublisherArchitectureNotificationPublisherProbe
  3. 发布通知后断言 probe 被调用且 handler 执行

测试结构清晰,覆盖了 PR 的核心场景。

Also applies to: 228-251, 287-348


33-33: 测试隔离:确保 SetUp 和 TearDown 对称重置。

SetUpTearDown 中都调用 AdditionalAssemblyNotificationHandlerState.Reset() 是良好的测试隔离实践,确保测试间的静态状态不会互相污染。这与 TrackingPipelineBehaviorLegacyBridgePipelineTracker 的重置模式保持一致。

Also applies to: 44-44


33-33: 所有引用的辅助类型均已正确定义。

AdditionalAssemblyNotificationHandlerStateAdditionalAssemblyNotificationAdditionalAssemblyNotificationHandlerRegistry 已分别在 GFramework.Core.Tests/Architectures/ 目录中以独立文件定义,无需任何改动。

Comment thread GFramework.Cqrs/Internal/CqrsDispatcher.cs
- 修复 CqrsDispatcher 默认通知发布器热路径的重复解析与默认实例重复分配

- 补充 strict IIocContainer 测试装配与通知发布器唯一注册断言

- 重构 CqrsDispatcherCacheTests 共享容器装配并更新 cqrs-rewrite 恢复文档
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 9, 2026

Summary

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Other ❓ Flaky 🍂 Duration ⏱️
2341 2341    ↑2 0    ↓2 0 0 0 37.8s    ↓703ms

Test Results

passed 2341 passed ↑2

Details

tests 2341 tests
clock 37.8s ↓703ms
tool nunit
build CI - Build & Test arrow-right build-and-test link #1099
pull-request Feat/cqrs optimization link #344

Insights

Average Tests per Run Total Flaky Tests Total Failed Slowest Test (p95)
2243 0 3 4.7s

Fail Rate

Fail Rate 0.00%
Test 📝 Results 📊 Passed ✅ Failed ❌ Fail Rate (%) 📈
CreateStream_Should_Throw_When_Stream_Pipeline_Behavior_Context_Does_Not_Implement_IArchitectureContext 11 10 1 9.09    ↓0.91
PublishAsync_Should_Stop_After_First_Handler_Exception_When_Using_Default_Publisher 29 28 1 3.45    ↓0.12
PublishAsync_Should_Throw_When_Context_Does_Not_Implement_IArchitectureContext 33 32 1 3.03    ↓0.10

build-and-test: Run #1099

Tests 📝 Passed ✅ Failed ❌ Skipped ⏭️ Pending ⏳ Other ❓ Flaky 🍂 Duration ⏱️
2341 2341 0 0 0 0 0 37.8s

🎉 All tests passed!

Slowest Tests

Test 📝 Results 📊 Duration (avg) ⏱️ Duration (p95) ⏱️
CreateStream_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 29 4.1s 4.7s
SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently 44 1.4s 4.7s
Does_Not_Report_When_FieldInjectedModel_Is_Registered 44 2.2s 2.3s
Generates_Scene_Behavior_Boilerplate 44 1.9s 2.0s
CleanupDuringAcquire_Should_NotCauseRaceCondition 44 1.1s 1.1s
Append_ShouldNotBlock 44 1.0s 1.0s
Context_Caching_Should_Improve_Performance 44 782ms 794ms
PendingCount_ShouldReflectQueuedEntries 44 501ms 501ms
Cleanup_Should_NotRemoveActiveLocks 44 404ms 405ms
Cleanup_Should_RemoveUnusedLocks 44 401ms 402ms

± Comparison with run #1098 at ddf878f | 🍂 No flaky tests detected across all runs. | ⏱️ Measured over 44 runs.

Github Test Reporter by CTRF 💚

@GeWuYou GeWuYou merged commit d85828c into main May 9, 2026
7 checks passed
@GeWuYou GeWuYou deleted the feat/cqrs-optimization branch May 9, 2026 04:25
@coderabbitai coderabbitai Bot mentioned this pull request May 9, 2026
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