Skip to content

Refactor/migrate cqrs to mediator#22

Merged
GeWuYou merged 21 commits into
mainfrom
refactor/migrate-CQRS-to-Mediator
Feb 14, 2026
Merged

Refactor/migrate cqrs to mediator#22
GeWuYou merged 21 commits into
mainfrom
refactor/migrate-CQRS-to-Mediator

Conversation

@GeWuYou

@GeWuYou GeWuYou commented Feb 14, 2026

Copy link
Copy Markdown
Owner

Summary by Sourcery

Integrate the Mediator library into the core architecture to provide a unified CQRS-style request pipeline while replacing the legacy IoC container with a Microsoft.Extensions.DependencyInjection-based implementation.

New Features:

  • Expose mediator-based request, command, query, notification, and streaming APIs on the architecture context and its abstractions, including convenience wrappers for common patterns.
  • Add architecture-level registration APIs for systems, models, utilities, and mediator pipeline behaviors that leverage the underlying DI container and optional service configurator hooks.
  • Introduce a MicrosoftDiContainer implementation of IIocContainer backed by IServiceCollection/IServiceProvider, enabling factory registration, behavior registration, and service hooks.

Enhancements:

  • Wire architecture initialization to run a configurable IServiceCollection hook, warn when mediator-based CQRS is not configured, and support waiting for readiness via a reusable task.
  • Provide CQRS helper base classes and Mediator pipeline behaviors (logging and performance) to standardize handler implementations and cross-cutting concerns across commands, queries, requests, streams, and notifications.
  • Refactor tests and architecture services to use the new MicrosoftDiContainer and add comprehensive mediator-focused test suites covering integration, behaviors, concurrency, cancellation, and coexistence with legacy CQRS APIs.

Tests:

  • Add extensive mediator integration, advanced features, and comprehensive behavior tests validating requests, notifications, streaming, cancellation, performance, error handling, and coexistence with legacy CQRS.
  • Update existing architecture, state machine, command, query, utility, and IoC tests to target MicrosoftDiContainer and the new mediator-aware context APIs.

- 将异步查询总线实例注释更正为异步查询执行器实例
- 为命令执行器添加返回值类型说明
- 为查询执行器添加返回值类型说明
- 为异步查询执行器添加返回值类型说明
- 移除多余空行并优化代码格式
- 实现Get(Type type)方法以获取指定类型的单个实例
- 实现GetRequired(Type type)方法以获取指定类型的必需实例
- 实现GetAll(Type type)方法以获取指定类型的所有实例
- 在抽象层添加对应的接口定义
- 为新方法添加完整的XML文档注释
- 添加全局using System.Runtime指令
- 移除原有的IocContainer实现
- 添加MicrosoftDiContainer作为Microsoft.Extensions.DependencyInjection的适配器
- 修改ArchitectureServices使用MicrosoftDiContainer替代IocContainer
- 更新所有相关测试类使用MicrosoftDiContainer
- 添加Mediator和Microsoft.Extensions.DependencyInjection包引用
- 扩展IIocContainer接口支持更多注册方法
- 在架构初始化中添加Mediator服务注册
- 为 Architecture 添加 RegisterSystem<T>、RegisterModel<T> 和 RegisterUtility<T> 泛型方法
- 支持通过 DI 容器自动创建实例并提供创建后回调钩子
- 在 MicrosoftDiContainer 中实现 RegisterPlurality<T> 方法
- 支持将单个实例注册到其实现的所有接口
- 更新 IIocContainer 接口定义以匹配新功能
- 为 RegisterFactory 方法添加 class 约束确保类型安全
- 移除OnContextReady方法,将日志记录器初始化改为直接赋值
- 将日志记录器改为readonly字段并直接初始化
- 修改Get<T>()方法以支持未冻结状态下的实例获取逻辑
- 修改Get(Type)方法以支持未冻结状态下的类型实例获取
- 更新GetAll<T>()方法以支持未冻结状态下的批量服务获取
- 更新GetAll(Type)方法以支持未冻结状态下的批量类型服务获取
- 移除EnsureProvider私有方法,内联检查逻辑到各个获取方法中
- 添加 Mediator 框架依赖注入配置
- 设置 Mediator 命名空间为 GFramework.Core.Mediator
- 配置 Mediator 服务生命周期为单例模式
- 设置生成类型为内部访问权限
- 配置通知发布器类型为 ForeachAwaitPublisher
- 添加必要的 using 语句引入 Mediator 和常量模块
- 移除直接的Mediator包依赖,改用Source Generator方式集成
- 添加RegisterMediator方法用于配置Mediator框架
- 添加RegisterMediatorBehavior方法用于注册管道行为
- 实现LoggingBehavior用于记录CQRS请求处理日志
- 实现PerformanceBehavior用于监控请求执行性能
- 更新架构配置以支持Mediator自定义配置
- 优化容器冻结检查的代码结构
- 实现了抽象命令处理器基类,支持带返回值和无返回值两种类型
- 创建了抽象通知处理器基类,提供统一的通知处理功能
- 添加了抽象查询处理器基类,支持泛型查询和结果类型
- 实现了抽象请求处理器基类,处理有响应和无响应的请求场景
- 集成了ContextAwareBase基类以提供上下文感知功能
- 使用Mediator库接口实现标准化的CQRS处理模式
- 新增 AbstractStreamCommandHandler 基类支持流式命令处理
- 新增 AbstractStreamQueryHandler 基类支持流式查询处理
- 新增 AbstractStreamRequestHandler 基类支持流式请求处理
- 所有基类继承 ContextAwareBase 并实现对应的流式处理器接口
- 提供统一的异步可枚举响应序列处理机制
- 支持取消令牌用于控制流式操作的执行过程
- 添加 Mediator 和 IPublisher 实例的延迟加载功能
- 实现 SendRequestAsync 方法用于发送 Command/Query 请求
- 添加 PublishAsync 方法用于发布通知事件
- 提供 CreateStream 方法支持流式数据处理
- 增加 SendAsync、QueryAsync、PublishEventAsync 等便捷扩展方法
- 更新 SendCommand 和 SendQuery 方法使用完整命名空间
- 在 IArchitectureContext 接口中添加 Mediator 相关方法定义
- 在ArchitectureContext中新增Sender属性和SendCommand/SendQuery方法
- 实现异步命令和查询的发送功能,支持取消令牌
- 提供同步版本的命令和查询发送方法以保持向后兼容性
- 更新IArchitectureContext接口定义新增相关方法签名
- 添加完整的Mediator集成测试验证新功能
- 配置项目依赖移除旧的Mediator包并添加必要引用
- 修复测试中的类型引用和方法重载问题
- 将 Mediator.IQuery<int> 替换为 IQuery<int> 类型引用
- 保持空值参数测试逻辑不变
- 确保类型转换正确性
- 移除专用的RegisterMediator方法,替换为ExecuteServicesHook通用服务配置方法
- 从架构配置中移除Mediator特定配置选项,改为通用服务配置委托
- 在架构基类中添加Configurator属性支持,允许子类提供自定义服务配置
- 更新测试代码适配新的服务配置方式,通过ExecuteServicesHook注册Mediator
- 移除过时的测试组件和相关验证逻辑
- 删除Mediator.SourceGenerator包引用,保留运行时依赖
- 添加WaitUntilReadyAsync方法的详细文档注释
- 实现多通知处理器调用验证功能
- 添加取消令牌对长运行请求的取消支持
- 实现流请求的取消令牌处理机制
- 添加并发请求干扰测试用例
- 实现处理器异常传播验证功能
- 添加多命令处理器共享对象修改测试
- 实现查询缓存功能测试用例
- 添加通知排序保持功能验证
- 实现流请求过滤功能测试
- 添加请求验证行为测试用例
- 实现性能基准测试功能
- 添加传统CQRS与Mediator共存测试
- 实现管道行为测试用例
- 添加高并发性能测试功能
- 实现内存使用稳定性测试
- 添加瞬态错误处理测试用例
- 实现熔断器模式测试功能
- 添加Saga模式一致性测试用例
- 实现请求链式依赖测试功能
- 添加外部服务依赖超时测试
- 实现数据库事务处理测试用例
- 添加架构上下文访问测试功能
- 实现服务检索功能测试用例
- 添加嵌套请求发送测试功能
- 实现生命周期管理测试用例
- 添加作用域服务隔离测试功能
- 实现错误传播测试用例
- 添加上下文性能开销测试功能
- 实现缓存性能提升测试用例
- 添加并发安全访问测试功能
- 实现状态一致性测试用例
- 添加系统集成测试功能
- 实现混合CQRS模式测试用例
- 将 OperationCanceledException 替换为更具体的 TaskCanceledException
- 修复瞬态错误处理器中的计数器逻辑,仅在 MaxErrors > 0 时递增
- 实现断路器功能,当失败次数达到阈值时打开断路器
- 添加Saga事务的补偿机制,在步骤失败时执行回滚操作
- 为验证行为添加输入验证逻辑
- 注册传统CQRS组件以支持混合模式测试
- 修复架构集成测试中的上下文访问问题
- [release ci]
@sourcery-ai

sourcery-ai Bot commented Feb 14, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduces Mediator-based CQRS integration and a new Microsoft.Extensions.DependencyInjection-backed IoC container, adds mediator pipeline behaviors and CQRS base handlers, wires Architectures and ArchitectureContext to Mediator while keeping legacy CQRS APIs, and updates tests and abstractions to support the new mediation and DI model.

Sequence diagram for Architecture initialization and Mediator wiring

sequenceDiagram
    actor App
    participant Architecture
    participant ArchitectureServices
    participant MicrosoftDiContainer
    participant IServiceCollection
    participant IMediator
    participant ArchitectureContext

    App->>Architecture: InitializeAsync()
    Architecture->>ArchitectureServices: new ArchitectureServices()
    ArchitectureServices->>MicrosoftDiContainer: new MicrosoftDiContainer()
    ArchitectureServices->>MicrosoftDiContainer: RegisterPlurality(EventBus, CommandExecutor, QueryExecutor, AsyncQueryExecutor, Environment)

    App->>Architecture: override Configurator
    note over Architecture: Configurator(IServiceCollection)

    Architecture->>ArchitectureServices: InitializeInternalAsync()
    ArchitectureServices->>MicrosoftDiContainer: Services.SetContext(Context)
    ArchitectureServices->>MicrosoftDiContainer: ExecuteServicesHook(Configurator)
    MicrosoftDiContainer->>IServiceCollection: configurator(IServiceCollection)
    IServiceCollection->>IMediator: AddMediator()

    ArchitectureServices->>MicrosoftDiContainer: Freeze()
    MicrosoftDiContainer->>MicrosoftDiContainer: BuildServiceProvider()

    ArchitectureServices->>ArchitectureContext: new ArchitectureContext(IIocContainer)
    ArchitectureContext->>MicrosoftDiContainer: Get<IMediator>()
    MicrosoftDiContainer-->>ArchitectureContext: IMediator
    Architecture-->>App: Ready
Loading

Sequence diagram for Mediator request handling via ArchitectureContext

sequenceDiagram
    actor Client
    participant ArchitectureContext
    participant MicrosoftDiContainer as Container
    participant IMediator
    participant IRequestHandler as Handler

    Client->>ArchitectureContext: SendRequestAsync(request)
    ArchitectureContext->>ArchitectureContext: GetOrCache<IMediator>()
    ArchitectureContext->>Container: Get<IMediator>()
    Container-->>ArchitectureContext: IMediator

    ArchitectureContext->>IMediator: Send(request, cancellationToken)
    IMediator->>Handler: Handle(request, cancellationToken)
    Handler-->>IMediator: response
    IMediator-->>ArchitectureContext: response
    ArchitectureContext-->>Client: response

    Client->>ArchitectureContext: PublishAsync(notification)
    ArchitectureContext->>ArchitectureContext: GetOrCache<IPublisher>()
    ArchitectureContext->>Container: Get<IPublisher>()
    Container-->>ArchitectureContext: IPublisher
    ArchitectureContext->>IPublisher: Publish(notification, cancellationToken)
Loading

Class diagram for MicrosoftDiContainer and IIocContainer extensions

classDiagram
    class ContextAwareBase

    class IIocContainer {
        <<interface>>
        +RegisterSingleton~T~(T instance)
        +RegisterSingleton~TService,TImpl~()
        +RegisterPlurality(object instance)
        +RegisterPlurality~T~()
        +RegisterSystem(ISystem system)
        +Register~T~(T instance)
        +Register(Type type, object instance)
        +RegisterFactory~TService~(IServiceProvider provider, TService factoryResult)
        +RegisterMediatorBehavior~TBehavior~()
        +ExecuteServicesHook(Action IServiceCollection configurator)
        +Get~T~() T
        +Get(Type type) object
        +GetRequired~T~() T
        +GetRequired(Type type) object
        +GetAll~T~() IReadOnlyList~T~
        +GetAll(Type type) IReadOnlyList~object~
        +GetAllSorted~T~(Comparison~T~ comparison) IReadOnlyList~T~
        +Contains~T~() bool
        +ContainsInstance(object instance) bool
        +Clear()
        +Freeze()
        +Services IServiceCollection
    }

    class MicrosoftDiContainer {
        -IServiceProvider provider
        -bool frozen
        -ReaderWriterLockSlim lock
        -HashSet~object~ registeredInstances
        -ILogger logger
        +MicrosoftDiContainer(IServiceCollection serviceCollection)
        +RegisterSingleton~T~(T instance)
        +RegisterSingleton~TService,TImpl~()
        +RegisterPlurality(object instance)
        +RegisterPlurality~T~()
        +RegisterSystem(ISystem system)
        +Register~T~(T instance)
        +Register(Type type, object instance)
        +RegisterFactory~TService~(Func IServiceProvider factory)
        +RegisterMediatorBehavior~TBehavior~()
        +ExecuteServicesHook(Action IServiceCollection configurator)
        +Get~T~() T
        +Get(Type type) object
        +GetRequired~T~() T
        +GetRequired(Type type) object
        +GetAll~T~() IReadOnlyList~T~
        +GetAll(Type type) IReadOnlyList~object~
        +GetAllSorted~T~(Comparison~T~ comparison) IReadOnlyList~T~
        +Contains~T~() bool
        +ContainsInstance(object instance) bool
        +Clear()
        +Freeze()
        +Services IServiceCollection
        -ThrowIfFrozen()
    }

    class IServiceCollection {
        <<interface>>
    }

    class IPipelineBehavior~TRequest,TResponse~ {
        <<interface>>
    }

    class LoggingBehavior~TRequest,TResponse~ {
        +Handle(TRequest message, MessageHandlerDelegate~TRequest,TResponse~ next, CancellationToken cancellationToken) TResponse
    }

    class PerformanceBehavior~TRequest,TResponse~ {
        +Handle(TRequest message, MessageHandlerDelegate~TRequest,TResponse~ next, CancellationToken cancellationToken) TResponse
    }

    IIocContainer <|.. MicrosoftDiContainer
    ContextAwareBase <|-- MicrosoftDiContainer

    IServiceCollection <.. MicrosoftDiContainer : wraps

    IPipelineBehavior~TRequest,TResponse~ <|.. LoggingBehavior~TRequest,TResponse~
    IPipelineBehavior~TRequest,TResponse~ <|.. PerformanceBehavior~TRequest,TResponse~

    MicrosoftDiContainer ..> IPipelineBehavior~TRequest,TResponse~ : RegisterMediatorBehavior
Loading

Class diagram for Mediator-based CQRS handlers and behaviors

classDiagram
    class ContextAwareBase

    %% Mediator interfaces
    class IRequest~TResponse~ {
        <<interface>>
    }
    class INotification {
        <<interface>>
    }
    class IStreamRequest~TResponse~ {
        <<interface>>
    }
    class ICommand~TResponse~ {
        <<interface>>
    }
    class IQuery~TResponse~ {
        <<interface>>
    }
    class ICommandHandler~TCommand~ {
        <<interface>>
        +Handle(TCommand command, CancellationToken cancellationToken) Unit
    }
    class ICommandHandler~TCommand,TResult~ {
        <<interface>>
        +Handle(TCommand command, CancellationToken cancellationToken) TResult
    }
    class IQueryHandler~TQuery,TResult~ {
        <<interface>>
        +Handle(TQuery query, CancellationToken cancellationToken) TResult
    }
    class IRequestHandler~TRequest~ {
        <<interface>>
        +Handle(TRequest request, CancellationToken cancellationToken) Unit
    }
    class IRequestHandler~TRequest,TResponse~ {
        <<interface>>
        +Handle(TRequest request, CancellationToken cancellationToken) TResponse
    }
    class INotificationHandler~TNotification~ {
        <<interface>>
        +Handle(TNotification notification, CancellationToken cancellationToken)
    }
    class IStreamRequestHandler~TRequest,TResponse~ {
        <<interface>>
        +Handle(TRequest request, CancellationToken cancellationToken) IAsyncEnumerable~TResponse~
    }
    class IStreamCommand~TResponse~ {
        <<interface>>
    }
    class IStreamQuery~TResponse~ {
        <<interface>>
    }
    class IStreamCommandHandler~TCommand,TResponse~ {
        <<interface>>
        +Handle(TCommand command, CancellationToken cancellationToken) IAsyncEnumerable~TResponse~
    }
    class IStreamQueryHandler~TQuery,TResponse~ {
        <<interface>>
        +Handle(TQuery query, CancellationToken cancellationToken) IAsyncEnumerable~TResponse~
    }

    %% Base handlers
    class AbstractCommandHandler~TCommand~ {
        <<abstract>>
        +Handle(TCommand command, CancellationToken cancellationToken) Unit
    }
    class AbstractCommandHandler~TCommand,TResult~ {
        <<abstract>>
        +Handle(TCommand command, CancellationToken cancellationToken) TResult
    }
    class AbstractQueryHandler~TQuery,TResult~ {
        <<abstract>>
        +Handle(TQuery query, CancellationToken cancellationToken) TResult
    }
    class AbstractRequestHandler~TRequest~ {
        <<abstract>>
        +Handle(TRequest request, CancellationToken cancellationToken) Unit
    }
    class AbstractRequestHandler~TRequest,TResponse~ {
        <<abstract>>
        +Handle(TRequest request, CancellationToken cancellationToken) TResponse
    }
    class AbstractNotificationHandler~TNotification~ {
        <<abstract>>
        +Handle(TNotification notification, CancellationToken cancellationToken)
    }
    class AbstractStreamCommandHandler~TCommand,TResponse~ {
        <<abstract>>
        +Handle(TCommand command, CancellationToken cancellationToken) IAsyncEnumerable~TResponse~
    }
    class AbstractStreamQueryHandler~TQuery,TResponse~ {
        <<abstract>>
        +Handle(TQuery query, CancellationToken cancellationToken) IAsyncEnumerable~TResponse~
    }
    class AbstractStreamRequestHandler~TRequest,TResponse~ {
        <<abstract>>
        +Handle(TRequest request, CancellationToken cancellationToken) IAsyncEnumerable~TResponse~
    }

    %% Behaviors
    class LoggingBehavior~TRequest,TResponse~ {
        +Handle(TRequest message, MessageHandlerDelegate~TRequest,TResponse~ next, CancellationToken cancellationToken) TResponse
    }
    class PerformanceBehavior~TRequest,TResponse~ {
        +Handle(TRequest message, MessageHandlerDelegate~TRequest,TResponse~ next, CancellationToken cancellationToken) TResponse
    }

    class IPipelineBehavior~TRequest,TResponse~ {
        <<interface>>
        +Handle(TRequest message, MessageHandlerDelegate~TRequest,TResponse~ next, CancellationToken cancellationToken) TResponse
    }

    %% Inheritance from ContextAwareBase
    ContextAwareBase <|-- AbstractCommandHandler~TCommand~
    ContextAwareBase <|-- AbstractCommandHandler~TCommand,TResult~
    ContextAwareBase <|-- AbstractQueryHandler~TQuery,TResult~
    ContextAwareBase <|-- AbstractRequestHandler~TRequest~
    ContextAwareBase <|-- AbstractRequestHandler~TRequest,TResponse~
    ContextAwareBase <|-- AbstractNotificationHandler~TNotification~
    ContextAwareBase <|-- AbstractStreamCommandHandler~TCommand,TResponse~
    ContextAwareBase <|-- AbstractStreamQueryHandler~TQuery,TResponse~
    ContextAwareBase <|-- AbstractStreamRequestHandler~TRequest,TResponse~

    %% Implementation relations
    ICommandHandler~TCommand~ <|.. AbstractCommandHandler~TCommand~
    ICommandHandler~TCommand,TResult~ <|.. AbstractCommandHandler~TCommand,TResult~
    IQueryHandler~TQuery,TResult~ <|.. AbstractQueryHandler~TQuery,TResult~
    IRequestHandler~TRequest~ <|.. AbstractRequestHandler~TRequest~
    IRequestHandler~TRequest,TResponse~ <|.. AbstractRequestHandler~TRequest,TResponse~
    INotificationHandler~TNotification~ <|.. AbstractNotificationHandler~TNotification~
    IStreamCommandHandler~TCommand,TResponse~ <|.. AbstractStreamCommandHandler~TCommand,TResponse~
    IStreamQueryHandler~TQuery,TResponse~ <|.. AbstractStreamQueryHandler~TQuery,TResponse~
    IStreamRequestHandler~TRequest,TResponse~ <|.. AbstractStreamRequestHandler~TRequest,TResponse~

    IPipelineBehavior~TRequest,TResponse~ <|.. LoggingBehavior~TRequest,TResponse~
    IPipelineBehavior~TRequest,TResponse~ <|.. PerformanceBehavior~TRequest,TResponse~
Loading

File-Level Changes

Change Details Files
Integrate Mediator into ArchitectureContext and IArchitectureContext to support unified command/query/request handling, notifications, and streams while preserving legacy CQRS APIs.
  • Add lazy-resolved Mediator, ISender, and IPublisher properties to ArchitectureContext using the IoC container cache.
  • Implement SendRequest[Async], SendCommand[Async], SendQuery[Async], PublishAsync, CreateStream, and convenience methods (SendAsync, QueryAsync, PublishEventAsync) in ArchitectureContext, delegating to Mediator interfaces with null checks and InvalidOperationException when not registered.
  • Expose corresponding methods in IArchitectureContext, updating command/query generic overloads to avoid type alias conflicts and keep legacy CQRS methods intact.
  • Adjust tests’ fake IArchitectureContext implementations to stub the new Mediator methods and to disambiguate legacy ICommand/IQuery usages.
GFramework.Core/architecture/ArchitectureContext.cs
GFramework.Core.Abstractions/architecture/IArchitectureContext.cs
GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs
GFramework.Core.Tests/architecture/GameContextTests.cs
Adopt a new MicrosoftDiContainer that wraps Microsoft.Extensions.DependencyInjection and extends the IoC abstraction and usage across architecture and tests.
  • Introduce MicrosoftDiContainer implementing IIocContainer with ServiceCollection-backed registration, freezing to build a ServiceProvider, thread-safe locking, instance tracking, and extended APIs for factories, mediator behaviors, service hooks, and typed Get/GetAll/GetRequired.
  • Extend IIocContainer with RegisterSingleton<TService,TImpl>, RegisterPlurality, RegisterFactory, RegisterMediatorBehavior, ExecuteServicesHook, Services property, and type-based Get/GetAll/GetRequired plus constrained Contains.
  • Replace direct IocContainer usage in ArchitectureServices, test contexts, and various tests with MicrosoftDiContainer and adjust logger field reflection to target the new container type.
  • Rename and update IoC tests to MicrosoftDiContainerTests and adjust their setup to work with MicrosoftDiContainer semantics.
GFramework.Core/ioc/MicrosoftDiContainer.cs
GFramework.Core.Abstractions/ioc/IIocContainer.cs
GFramework.Core/architecture/ArchitectureServices.cs
GFramework.Core.Tests/ioc/MicrosoftDiContainerTests.cs
GFramework.Core.Tests/architecture/ArchitectureContextTests.cs
GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs
GFramework.Core.Tests/architecture/GameContextTests.cs
GFramework.Core.Tests/state/StateMachineSystemTests.cs
GFramework.Core.Tests/command/AbstractAsyncCommandTests.cs
GFramework.Core.Tests/query/AbstractAsyncQueryTests.cs
GFramework.Core.Tests/utility/AbstractContextUtilityTests.cs
Enhance Architecture to support DI-created systems/models/utilities, mediator behaviors, and service configuration hooks for Mediator-based CQRS.
  • Add RegisterMediatorBehavior to Architecture that delegates to the container and logs registrations.
  • Introduce RegisterSystem, RegisterModel, and RegisterUtility overloads that register factories with ActivatorUtilities.CreateInstance, set architecture context, register lifecycle components, and invoke optional onCreated hooks.
  • Add a virtual Configurator property of type Action? to IArchitecture and Architecture, executed during initialization via Container.ExecuteServicesHook and accompanied by a log warning when null about mediator-based CQRS not taking effect.
  • Update WaitUntilReadyAsync XML docs and behavior to short-circuit when already ready.
GFramework.Core/architecture/Architecture.cs
GFramework.Core.Abstractions/architecture/IArchitecture.cs
GFramework.Core.Abstractions/architecture/IArchitectureServices.cs
Add Mediator-focused CQRS base types and pipeline behaviors for logging and performance, and generate Mediator source files for integration.
  • Introduce LoggingBehavior and PerformanceBehavior implementing Mediator IPipelineBehavior to log request handling and warn on slow (>500ms) requests.
  • Add abstract base handlers for commands, queries, requests, notifications, and stream variants that extend ContextAwareBase and wrap Mediator handler interfaces.
  • Include generated Mediator source-generator output files under GFramework.SourceGenerators/Generated to support Mediator integration.
GFramework.Core/cqrs/behaviors/LoggingBehavior.cs
GFramework.Core/cqrs/behaviors/PerformanceBehavior.cs
GFramework.Core/cqrs/command/AbstractCommandHandler.cs
GFramework.Core/cqrs/command/AbstractStreamCommandHandler.cs
GFramework.Core/cqrs/query/AbstractQueryHandler.cs
GFramework.Core/cqrs/query/AbstractStreamQueryHandler.cs
GFramework.Core/cqrs/request/AbstractRequestHandler.cs
GFramework.Core/cqrs/request/AbstractStreamRequestHandler.cs
GFramework.Core/cqrs/notification/AbstractNotificationHandler.cs
GFramework.SourceGenerators/Generated/Mediator.SourceGenerator/Mediator.SourceGenerator.IncrementalMediatorGenerator/AssemblyReference.g.cs
GFramework.SourceGenerators/Generated/Mediator.SourceGenerator/Mediator.SourceGenerator.IncrementalMediatorGenerator/Mediator.g.cs
GFramework.SourceGenerators/Generated/Mediator.SourceGenerator/Mediator.SourceGenerator.IncrementalMediatorGenerator/MediatorOptions.g.cs
GFramework.SourceGenerators/Generated/Mediator.SourceGenerator/Mediator.SourceGenerator.IncrementalMediatorGenerator/MediatorOptionsAttribute.g.cs
Add comprehensive Mediator integration and advanced feature tests to validate behavior, performance, and coexistence with legacy CQRS.
  • Add MediatorComprehensiveTests covering SendRequest[Async], Send/Query helpers, Publish, streams, cancellation, concurrency, handler exceptions, caching, ordering, and coexistence with legacy CQRS.
  • Add MediatorArchitectureIntegrationTests focusing on context integration, service retrieval, nested requests, lifecycle semantics, scoped/consistent state, and mixed CQRS patterns.
  • Add MediatorAdvancedFeaturesTests validating validation, retry/circuit-breaker/saga-style patterns, high concurrency, memory usage, chaining, external dependency timeouts, and database-style operations in a simplified form.
  • Update test architectures/contexts to implement new IArchitectureContext Mediator members, typically throwing NotImplementedException for unused paths.
GFramework.Core.Tests/mediator/MediatorComprehensiveTests.cs
GFramework.Core.Tests/mediator/MediatorArchitectureIntegrationTests.cs
GFramework.Core.Tests/mediator/MediatorAdvancedFeaturesTests.cs
GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs
GFramework.Core.Tests/architecture/GameContextTests.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 5 issues, and left some high level feedback:

  • In IArchitectureContext the signatures TResult SendCommand<TResult>(command.ICommand<TResult> command) and TResult SendQuery<TResult>(query.IQuery<TResult> query) are invalid/ambiguous C# (and inconsistent with the concrete implementation), they should be corrected to the fully qualified abstractions namespace or the existing alias.
  • In MicrosoftDiContainer, some registration methods (e.g. RegisterSingleton<TService,TImpl>, RegisterFactory) call ThrowIfFrozen without taking the write lock while others are wrapped in _lock.EnterWriteLock/ExitWriteLock; consider consistently guarding all mutating operations with the same lock to avoid races and keep the thread-safety guarantees coherent.
  • LoggingBehavior and PerformanceBehavior call next and immediately return its ValueTask without awaiting; this means the stopwatch in PerformanceBehavior only measures the synchronous portion and the try/catch in LoggingBehavior will not see asynchronous exceptions—consider making these handlers async, awaiting next, and then doing timing/logging around the awaited call.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In IArchitectureContext the signatures `TResult SendCommand<TResult>(command.ICommand<TResult> command)` and `TResult SendQuery<TResult>(query.IQuery<TResult> query)` are invalid/ambiguous C# (and inconsistent with the concrete implementation), they should be corrected to the fully qualified abstractions namespace or the existing alias.
- In MicrosoftDiContainer, some registration methods (e.g. RegisterSingleton<TService,TImpl>, RegisterFactory<TService>) call ThrowIfFrozen without taking the write lock while others are wrapped in _lock.EnterWriteLock/ExitWriteLock; consider consistently guarding all mutating operations with the same lock to avoid races and keep the thread-safety guarantees coherent.
- LoggingBehavior and PerformanceBehavior call `next` and immediately return its ValueTask without awaiting; this means the stopwatch in PerformanceBehavior only measures the synchronous portion and the try/catch in LoggingBehavior will not see asynchronous exceptions—consider making these handlers async, awaiting `next`, and then doing timing/logging around the awaited call.

## Individual Comments

### Comment 1
<location> `GFramework.Core.Abstractions/architecture/IArchitectureContext.cs:58-61` </location>
<code_context>
     /// <param name="command">要发送的命令</param>
     /// <returns>命令执行结果</returns>
-    TResult SendCommand<TResult>(ICommand<TResult> command);
+    TResult SendCommand<TResult>(command.ICommand<TResult> command);

     /// <summary>
</code_context>

<issue_to_address>
**issue (bug_risk):** The `command.ICommand` and `query.IQuery` type references look incorrect and will not resolve.

These signatures now reference `command.ICommand<TResult>` / `query.IQuery<TResult>`, but no `command` or `query` namespace is in scope. Elsewhere you either use the alias `ICommand = GFramework.Core.Abstractions.command.ICommand` or fully qualify as `Abstractions.command.ICommand` / `Abstractions.query.IQuery`. To compile and stay consistent, these should instead be:

```csharp
TResult SendCommand<TResult>(Abstractions.command.ICommand<TResult> command);
TResult SendQuery<TResult>(Abstractions.query.IQuery<TResult> query);
// or, if the alias/usings are present
TResult SendCommand<TResult>(ICommand<TResult> command);
TResult SendQuery<TResult>(IQuery<TResult> query);
```
</issue_to_address>

### Comment 2
<location> `GFramework.Core/cqrs/behaviors/LoggingBehavior.cs:40-49` </location>
<code_context>
+    /// <param name="next">下一个处理委托,用于继续管道执行</param>
+    /// <param name="cancellationToken">取消令牌,用于取消操作</param>
+    /// <returns>处理结果的ValueTask</returns>
+    public ValueTask<TResponse> Handle(TRequest message, MessageHandlerDelegate<TRequest, TResponse> next,
+        CancellationToken cancellationToken)
+    {
+        var requestName = typeof(TRequest).Name;
+
+        _logger.Debug($"Handling {requestName}");
+
+        try
+        {
+            var response = next(message, cancellationToken);
+            _logger.Debug($"Handled {requestName} successfully");
+            return response;
</code_context>

<issue_to_address>
**issue (bug_risk):** The logging behavior returns the `ValueTask` from `next` without awaiting, so exceptions and timing are not captured correctly.

Since `Handle` just returns `next(message, cancellationToken)` without awaiting, the "Handled ... successfully" message is logged before the pipeline step actually completes, and any asynchronous exceptions from `next` will not be logged here. This also makes timing and completion-based behavior inaccurate.

Make `Handle` async and await `next` so logging and exception handling reflect real completion:

```csharp
public async ValueTask<TResponse> Handle(
    TRequest message,
    MessageHandlerDelegate<TRequest, TResponse> next,
    CancellationToken cancellationToken)
{
    var requestName = typeof(TRequest).Name;
    _logger.Debug($"Handling {requestName}");

    try
    {
        var response = await next(message, cancellationToken);
        _logger.Debug($"Handled {requestName} successfully");
        return response;
    }
    catch (Exception ex)
    {
        _logger.Error($"Error handling {requestName}", ex);
        throw;
    }
}
```
</issue_to_address>

### Comment 3
<location> `GFramework.Core/cqrs/behaviors/PerformanceBehavior.cs:41-50` </location>
<code_context>
+    /// <param name="next">下一个处理委托,用于继续管道执行</param>
+    /// <param name="cancellationToken">取消令牌,用于取消操作</param>
+    /// <returns>处理结果的ValueTask</returns>
+    public ValueTask<TResponse> Handle(TRequest message, MessageHandlerDelegate<TRequest, TResponse> next,
+        CancellationToken cancellationToken)
+    {
</code_context>

<issue_to_address>
**issue (performance):** The performance behavior stops the stopwatch before the async request has actually completed.

Here `stopwatch.Stop()` runs right after `var response = next(message, cancellationToken);`, before the `ValueTask` is awaited. As a result, you only measure the time to *start* the handler, not its full execution, so slow handlers won’t be logged reliably.

You likely want an `async` handler, e.g.:

```csharp
public async ValueTask<TResponse> Handle(
    TRequest message,
    MessageHandlerDelegate<TRequest, TResponse> next,
    CancellationToken cancellationToken)
{
    var stopwatch = Stopwatch.StartNew();

    var response = await next(message, cancellationToken);

    stopwatch.Stop();
    var elapsedMilliseconds = stopwatch.ElapsedMilliseconds;

    if (elapsedMilliseconds > 500)
    {
        var requestName = typeof(TRequest).Name;
        _logger.Warn($"Long Running Request: {requestName} ({elapsedMilliseconds} ms)");
    }

    return response;
}
```

This records the true end-to-end handler duration.
</issue_to_address>

### Comment 4
<location> `GFramework.Core/architecture/Architecture.cs:37-41` </location>
<code_context>
+    ///     用于配置Mediator框架的行为拦截和处理逻辑
+    /// </summary>
+    /// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
+    public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
+    {
+        _logger.Debug($"Registering mediator behavior: {typeof(TBehavior).Name}");
+        Container.RegisterPlurality<TBehavior>();
+    }
+
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Mediator behaviors are registered via `RegisterPlurality` instead of the dedicated `RegisterMediatorBehavior` hook on the container.

`IIocContainer` now provides `RegisterMediatorBehavior<TBehavior>()` to register behaviors as `IPipelineBehavior<,>`. Calling `RegisterPlurality<TBehavior>()` here only registers the concrete type and its interfaces, so Mediator’s expected `IPipelineBehavior<,>` open generic registration is missing.

To use the intended abstraction and ensure behaviors are wired correctly, delegate to the container hook instead:

```csharp
public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
{
    _logger.Debug($"Registering mediator behavior: {typeof(TBehavior).Name}");
    Container.RegisterMediatorBehavior<TBehavior>();
}
```

```suggestion
    public void RegisterMediatorBehavior<TBehavior>() where TBehavior : class
    {
        _logger.Debug($"Registering mediator behavior: {typeof(TBehavior).Name}");
        Container.RegisterMediatorBehavior<TBehavior>();
    }
```
</issue_to_address>

### Comment 5
<location> `GFramework.Core.Abstractions/ioc/IIocContainer.cs:29-33` </location>
<code_context>
+    /// </summary>
+    /// <typeparam name="TService">服务接口或基类类型</typeparam>
+    /// <typeparam name="TImpl">具体的实现类型</typeparam>
+    void RegisterSingleton<TService, TImpl>()
+        where TImpl : class, TService where TService : class;

</code_context>

<issue_to_address>
**issue (bug_risk):** Some new registration APIs in `IIocContainer` are not respecting the frozen state or lock symmetry in the implementation.

In `MicrosoftDiContainer`, existing mutating methods (e.g. `RegisterSingleton<T>`, `RegisterPlurality`, `Register`) use the write lock and `ThrowIfFrozen()` to prevent changes after freeze. The newer APIs:

- `RegisterSingleton<TService, TImpl>()`
- `RegisterFactory<TService>(...)`
- `ExecuteServicesHook(...)`

don’t follow this pattern. For instance, `RegisterSingleton<TService, TImpl>()` calls `Services.AddSingleton` directly, and `ExecuteServicesHook` can still mutate `Services` after `Freeze()` has built the provider.

These should use the same write-lock + `ThrowIfFrozen()` pattern, and `ExecuteServicesHook` should be explicitly decided as pre-freeze only (and guarded) or documented as an exception, to keep the container’s immutability guarantees and avoid race conditions or post-freeze mutations.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread GFramework.Core.Abstractions/architecture/IArchitectureContext.cs
Comment thread GFramework.Core/cqrs/behaviors/LoggingBehavior.cs Outdated
Comment thread GFramework.Core/cqrs/behaviors/PerformanceBehavior.cs Outdated
Comment thread GFramework.Core/architecture/Architecture.cs
Comment thread GFramework.Core.Abstractions/ioc/IIocContainer.cs
- 移除ICommand类型的using别名,直接使用完整命名空间
- 将SendCommand方法参数类型从ICommand改为command.ICommand
- 添加Mediator模式的同步命令处理方法兼容性接口
- 添加Mediator模式的异步命令处理方法支持取消令牌
- 添加Mediator模式的同步查询处理方法兼容性接口
- 添加Mediator模式的异步查询处理方法支持取消令牌
- 移除重复的Mediator相关方法声明,优化接口结构
- 添加Stopwatch用于精确测量请求处理时间
- 将Handle方法改为异步方法并正确await next调用
- 在成功和失败情况下都记录详细的耗时信息
- 添加对OperationCanceledException的特殊处理和日志记录
- 改进异常处理逻辑以包含执行时间信息
- 将同步方法改为异步方法以正确处理异步操作
- 使用 Stopwatch.GetTimestamp() 替代 StartNew() 提高计时精度
- 通过 try-finally 确保即使在异常情况下也能正确计算执行时间
- 改进长时间运行请求的日志记录机制
- 使用 Elapsed.TotalMilliseconds 替代 ElapsedMilliseconds 并保留两位小数
- 保持 500 毫秒阈值不变,只对超时请求记录警告日志
- 将 Container.RegisterPlurality<TBehavior>() 替换为 Container.RegisterMediatorBehavior<TBehavior>()
- 修正了中介者行为注册的 API 调用错误
- 在RegisterSingleton方法中添加读写锁保护
- 在RegisterFactory方法中添加读写锁保护
- 在ExecuteServicesHook方法中添加读写锁保护
- 确保在冻结状态下抛出异常
- 添加日志记录单例注册操作
- 统一异常处理和资源清理逻辑
- 将MicrosoftDiContainer中的Services属性重命名为GetServicesUnsafe
- 将IIocContainer接口中的Services属性重命名为GetServicesUnsafe
- 更新所有使用Services的地方为GetServicesUnsafe
- 保持原有的功能不变,仅修改属性名称以避免直接访问风险
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