diff --git a/.coderabbit.yaml b/.coderabbit.yaml
index 8cb624ec..5fc95d9d 100644
--- a/.coderabbit.yaml
+++ b/.coderabbit.yaml
@@ -16,6 +16,8 @@ reviews:
auto_review:
enabled: true
drafts: false # draft PR 不 review
+ base_branches:
+ - refactor/cqrs-architecture-decoupling
chat:
auto_reply: true
diff --git a/AGENTS.md b/AGENTS.md
index c63a2bfa..720b53af 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -244,6 +244,16 @@ bash scripts/validate-csharp-naming.sh
- Tracking updates MUST reflect completed work, newly discovered issues, validation results, and the next recommended
recovery point.
- Completing code changes without updating the active tracking document is considered incomplete work.
+- For any multi-step refactor, migration, or cross-module task, contributors MUST create or adopt a dedicated recovery
+ document under `local-plan/todos/` before making substantive code changes.
+- Recovery documents MUST record the current phase, the active recovery point identifier, known risks, and the next
+ recommended resume step so another contributor or subagent can continue the work safely.
+- Contributors MUST maintain a matching execution trace under `local-plan/traces/` for complex work. The trace should
+ record the current date, key decisions, validation milestones, and the immediate next step.
+- When a task spans multiple commits or is likely to exceed a single agent context window, update both the recovery
+ document and the trace at each meaningful milestone before pausing or handing work off.
+- If subagents are used on a complex task, the main agent MUST capture the delegated scope and any accepted findings in
+ the active recovery document or trace before continuing implementation.
### Repository Documentation
diff --git a/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs b/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs
index 3469d8db..b9b5dc9a 100644
--- a/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs
+++ b/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs
@@ -1,18 +1,23 @@
using GFramework.Core.Abstractions.Command;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Abstractions.Events;
using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Query;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility;
-using Mediator;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Abstractions.Architectures;
///
-/// 架构上下文接口,提供对系统、模型、工具类的访问以及命令、查询、事件的发送和注册功能
+/// 架构上下文接口,统一暴露框架组件访问、兼容旧命令/查询总线,以及当前推荐的 CQRS 运行时入口。
///
+///
+/// 旧的 GFramework.Core.Abstractions.Command 与 GFramework.Core.Abstractions.Query 契约会继续通过原有 Command/Query Executor 路径执行,以保证存量代码兼容。
+/// 新的 GFramework.Core.Abstractions.Cqrs 契约由内置 CQRS dispatcher 统一处理,支持 request pipeline、notification publish 与 stream request。
+/// 新功能优先使用 、 与对应的 CQRS Command/Query 重载;迁移旧代码时可先保留旧入口,再逐步替换为 CQRS 请求模型。
+///
public interface IArchitectureContext
{
///
@@ -104,87 +109,91 @@ public interface IArchitectureContext
IReadOnlyList GetUtilitiesByPriority() where TUtility : class, IUtility;
///
- /// 发送一个命令
+ /// 发送一个旧版命令。
///
- /// 要发送的命令
+ /// 要发送的旧版命令。
void SendCommand(ICommand command);
///
- /// 发送一个带返回值的命令
+ /// 发送一个旧版带返回值命令。
///
- /// 命令执行结果类型
- /// 要发送的命令
- /// 命令执行结果
- TResult SendCommand(Command.ICommand command);
+ /// 命令执行结果类型。
+ /// 要发送的旧版命令。
+ /// 命令执行结果。
+ TResult SendCommand(ICommand command);
///
- /// [Mediator] 发送命令的同步版本(不推荐,仅用于兼容性)
+ /// 发送一个新版 CQRS 命令并返回结果。
///
- /// 命令响应类型
- /// 要发送的命令对象
- /// 命令执行结果
- TResponse SendCommand(Mediator.ICommand command);
+ /// 命令响应类型。
+ /// 要发送的 CQRS 命令。
+ /// 命令执行结果。
+ ///
+ /// 这是迁移后的推荐命令入口。无返回值命令应实现 IRequest<Unit>,并优先通过 调用。
+ ///
+ TResponse SendCommand(Cqrs.Command.ICommand command);
///
- /// 发送并异步执行一个命令
+ /// 异步发送一个旧版命令。
///
- /// 要发送的命令
+ /// 要发送的旧版命令。
Task SendCommandAsync(IAsyncCommand command);
///
- /// [Mediator] 异步发送命令并返回结果
- /// 通过Mediator模式发送命令请求,支持取消操作
+ /// 异步发送一个新版 CQRS 命令并返回结果。
///
- /// 命令响应类型
- /// 要发送的命令对象
- /// 取消令牌,用于取消操作
- /// 包含命令执行结果的ValueTask
- ValueTask SendCommandAsync(Mediator.ICommand command,
+ /// 命令响应类型。
+ /// 要发送的 CQRS 命令。
+ /// 取消令牌。
+ /// 包含命令执行结果的值任务。
+ ValueTask SendCommandAsync(Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default);
///
- /// 发送并异步执行一个带返回值的命令
+ /// 异步发送一个旧版带返回值命令。
///
- /// 命令执行结果类型
- /// 要发送的命令
- /// 命令执行结果
+ /// 命令执行结果类型。
+ /// 要发送的旧版命令。
+ /// 命令执行结果。
Task SendCommandAsync(IAsyncCommand command);
///
- /// 发送一个查询请求
+ /// 发送一个旧版查询请求。
///
- /// 查询结果类型
- /// 要发送的查询
- /// 查询结果
- TResult SendQuery(Query.IQuery query);
+ /// 查询结果类型。
+ /// 要发送的旧版查询。
+ /// 查询结果。
+ TResult SendQuery(IQuery query);
///
- /// [Mediator] 发送查询的同步版本(不推荐,仅用于兼容性)
+ /// 发送一个新版 CQRS 查询并返回结果。
///
- /// 查询响应类型
- /// 要发送的查询对象
- /// 查询结果
- TResponse SendQuery(Mediator.IQuery query);
+ /// 查询响应类型。
+ /// 要发送的 CQRS 查询。
+ /// 查询结果。
+ ///
+ /// 这是迁移后的推荐查询入口。新查询应优先实现 GFramework.Core.Abstractions.Cqrs.Query.IQuery<TResponse>。
+ ///
+ TResponse SendQuery(Cqrs.Query.IQuery query);
///
- /// 异步发送一个查询请求
+ /// 异步发送一个旧版查询请求。
///
- /// 查询结果类型
- /// 要发送的异步查询
- /// 查询结果
+ /// 查询结果类型。
+ /// 要发送的旧版异步查询。
+ /// 查询结果。
Task SendQueryAsync(IAsyncQuery query);
///
- /// [Mediator] 异步发送查询并返回结果
- /// 通过Mediator模式发送查询请求,支持取消操作
+ /// 异步发送一个新版 CQRS 查询并返回结果。
///
- /// 查询响应类型
- /// 要发送的查询对象
- /// 取消令牌,用于取消操作
- /// 包含查询结果的ValueTask
- ValueTask SendQueryAsync(Mediator.IQuery query,
+ /// 查询响应类型。
+ /// 要发送的 CQRS 查询。
+ /// 取消令牌。
+ /// 包含查询结果的值任务。
+ ValueTask SendQueryAsync(Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default);
///
@@ -216,28 +225,40 @@ public interface IArchitectureContext
void UnRegisterEvent(Action onEvent);
///
- /// 发送请求(统一处理 Command/Query)
+ /// 发送新版 CQRS 请求,并统一处理命令与查询。
///
+ ///
+ /// 这是自有 CQRS 运行时的主入口。新代码应优先通过该方法或 进入 dispatcher。
+ ///
ValueTask SendRequestAsync(
IRequest request,
CancellationToken cancellationToken = default);
///
- /// 发送请求(同步版本,不推荐)
+ /// 发送新版 CQRS 请求的同步包装版本。
///
+ ///
+ /// 仅为兼容同步调用链保留;新代码应优先使用异步入口,避免阻塞当前线程。
+ ///
TResponse SendRequest(IRequest request);
///
- /// 发布通知(一对多事件)
+ /// 发布新版 CQRS 通知。
///
+ ///
+ /// 该入口用于一对多通知分发,与框架级 EventBus 事件系统并存,适合围绕请求处理过程传播领域通知。
+ ///
ValueTask PublishAsync(
TNotification notification,
CancellationToken cancellationToken = default)
where TNotification : INotification;
///
- /// 创建流式请求(用于大数据集)
+ /// 创建新版 CQRS 流式请求。
///
+ ///
+ /// 适用于需要按序惰性产出大量结果的场景。调用方应消费返回的异步序列,而不是回退到旧版查询总线。
+ ///
IAsyncEnumerable CreateStream(
IStreamRequest request,
CancellationToken cancellationToken = default);
@@ -245,7 +266,7 @@ public interface IArchitectureContext
// === 便捷扩展方法 ===
///
- /// 发送命令(无返回值)
+ /// 发送一个无返回值的新版 CQRS 命令。
///
ValueTask SendAsync(
TCommand command,
@@ -253,7 +274,7 @@ public interface IArchitectureContext
where TCommand : IRequest;
///
- /// 发送命令(有返回值)
+ /// 发送一个有返回值的新版 CQRS 请求。
///
ValueTask SendAsync(
IRequest command,
@@ -265,4 +286,4 @@ public interface IArchitectureContext
///
/// 环境对象实例
IEnvironment GetEnvironment();
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/Command/ICommand.cs b/GFramework.Core.Abstractions/Cqrs/Command/ICommand.cs
new file mode 100644
index 00000000..ba04331e
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/Command/ICommand.cs
@@ -0,0 +1,25 @@
+namespace GFramework.Core.Abstractions.Cqrs.Command;
+
+///
+/// 表示一个 CQRS 命令。
+/// 命令通常用于修改系统状态。
+///
+/// 命令响应类型。
+public interface ICommand : IRequest
+{
+}
+
+///
+/// 表示一个无显式返回值的 CQRS 命令。
+///
+public interface ICommand : ICommand
+{
+}
+
+///
+/// 表示一个流式 CQRS 命令。
+///
+/// 流式响应元素类型。
+public interface IStreamCommand : IStreamRequest
+{
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/INotification.cs b/GFramework.Core.Abstractions/Cqrs/INotification.cs
new file mode 100644
index 00000000..9d69e28e
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/INotification.cs
@@ -0,0 +1,9 @@
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 表示一个一对多发布的通知消息。
+/// 通知不要求返回值,允许被零个或多个处理器消费。
+///
+public interface INotification
+{
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/INotificationHandler.cs b/GFramework.Core.Abstractions/Cqrs/INotificationHandler.cs
new file mode 100644
index 00000000..23861d1d
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/INotificationHandler.cs
@@ -0,0 +1,17 @@
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 表示处理通知消息的处理器契约。
+///
+/// 通知类型。
+public interface INotificationHandler
+ where TNotification : INotification
+{
+ ///
+ /// 处理通知消息。
+ ///
+ /// 要处理的通知。
+ /// 取消令牌。
+ /// 异步处理任务。
+ ValueTask Handle(TNotification notification, CancellationToken cancellationToken);
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/IPipelineBehavior.cs b/GFramework.Core.Abstractions/Cqrs/IPipelineBehavior.cs
new file mode 100644
index 00000000..cd01aad3
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/IPipelineBehavior.cs
@@ -0,0 +1,22 @@
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 定义 CQRS 请求处理前后的管道行为。
+///
+/// 请求类型。
+/// 响应类型。
+public interface IPipelineBehavior
+ where TRequest : IRequest
+{
+ ///
+ /// 处理当前请求,并决定是否继续调用后续行为或最终处理器。
+ ///
+ /// 当前请求消息。
+ /// 下一个处理委托。
+ /// 取消令牌。
+ /// 请求响应。
+ ValueTask Handle(
+ TRequest message,
+ MessageHandlerDelegate next,
+ CancellationToken cancellationToken);
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/IRequest.cs b/GFramework.Core.Abstractions/Cqrs/IRequest.cs
new file mode 100644
index 00000000..26259fc4
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/IRequest.cs
@@ -0,0 +1,10 @@
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 表示一个有响应的 CQRS 请求。
+/// 该接口是命令、查询以及其他请求语义的统一基接口。
+///
+/// 请求响应类型。
+public interface IRequest
+{
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/IRequestHandler.cs b/GFramework.Core.Abstractions/Cqrs/IRequestHandler.cs
new file mode 100644
index 00000000..2415e282
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/IRequestHandler.cs
@@ -0,0 +1,18 @@
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 表示处理单个 CQRS 请求的处理器契约。
+///
+/// 请求类型。
+/// 响应类型。
+public interface IRequestHandler
+ where TRequest : IRequest
+{
+ ///
+ /// 处理指定请求并返回结果。
+ ///
+ /// 要处理的请求。
+ /// 取消令牌。
+ /// 请求结果。
+ ValueTask Handle(TRequest request, CancellationToken cancellationToken);
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/IStreamRequest.cs b/GFramework.Core.Abstractions/Cqrs/IStreamRequest.cs
new file mode 100644
index 00000000..05ffa5df
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/IStreamRequest.cs
@@ -0,0 +1,10 @@
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 表示一个流式 CQRS 请求。
+/// 请求处理器可以逐步产生响应序列,而不是一次性返回完整结果。
+///
+/// 流式响应元素类型。
+public interface IStreamRequest
+{
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/IStreamRequestHandler.cs b/GFramework.Core.Abstractions/Cqrs/IStreamRequestHandler.cs
new file mode 100644
index 00000000..1c6e02a7
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/IStreamRequestHandler.cs
@@ -0,0 +1,18 @@
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 表示处理流式 CQRS 请求的处理器契约。
+///
+/// 流式请求类型。
+/// 流式响应元素类型。
+public interface IStreamRequestHandler
+ where TRequest : IStreamRequest
+{
+ ///
+ /// 处理流式请求并返回异步响应序列。
+ ///
+ /// 要处理的请求。
+ /// 取消令牌。
+ /// 异步响应序列。
+ IAsyncEnumerable Handle(TRequest request, CancellationToken cancellationToken);
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/MessageHandlerDelegate.cs b/GFramework.Core.Abstractions/Cqrs/MessageHandlerDelegate.cs
new file mode 100644
index 00000000..520f9fee
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/MessageHandlerDelegate.cs
@@ -0,0 +1,19 @@
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 表示 CQRS 请求在管道中继续向下执行的处理委托。
+///
+///
+/// 管道行为可以通过不调用该委托来短路请求处理。
+/// 除显式实现重试等高级语义外,行为通常应最多调用一次该委托,以维持单次请求分发的确定性。
+/// 调用方应传递当前收到的 ,确保取消信号沿整条管道一致传播。
+///
+/// 请求类型。
+/// 响应类型。
+/// 当前请求消息。
+/// 取消令牌。
+/// 请求响应。
+public delegate ValueTask MessageHandlerDelegate(
+ TRequest message,
+ CancellationToken cancellationToken)
+ where TRequest : IRequest;
diff --git a/GFramework.Core.Abstractions/Cqrs/Query/IQuery.cs b/GFramework.Core.Abstractions/Cqrs/Query/IQuery.cs
new file mode 100644
index 00000000..cbb1586e
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/Query/IQuery.cs
@@ -0,0 +1,18 @@
+namespace GFramework.Core.Abstractions.Cqrs.Query;
+
+///
+/// 表示一个 CQRS 查询。
+/// 查询用于读取数据,不应产生副作用。
+///
+/// 查询响应类型。
+public interface IQuery : IRequest
+{
+}
+
+///
+/// 表示一个流式 CQRS 查询。
+///
+/// 流式响应元素类型。
+public interface IStreamQuery : IStreamRequest
+{
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/Unit.cs b/GFramework.Core.Abstractions/Cqrs/Unit.cs
new file mode 100644
index 00000000..7dc3da14
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/Unit.cs
@@ -0,0 +1,13 @@
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 表示没有实际返回值的 CQRS 响应类型。
+/// 该类型用于统一命令与请求的泛型签名,避免引入外部库的 Unit 定义。
+///
+public readonly record struct Unit
+{
+ ///
+ /// 获取默认的空响应实例。
+ ///
+ public static Unit Value { get; } = new();
+}
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
index 05601230..bf6566b4 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
@@ -2,14 +2,13 @@ using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Utility;
using GFramework.Core.Architectures;
using GFramework.Core.Logging;
-using Mediator;
-using Microsoft.Extensions.DependencyInjection;
+using GfCqrs = GFramework.Core.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Architectures;
///
-/// 验证 Architecture 通过 ArchitectureModules 暴露出的模块安装与 Mediator 行为注册能力。
-/// 这些测试覆盖模块安装回调和中介管道行为接入,确保模块管理器仍然保持可观察行为不变。
+/// 验证 Architecture 通过 ArchitectureModules 暴露出的模块安装与 CQRS 行为注册能力。
+/// 这些测试覆盖模块安装回调和请求管道行为接入,确保模块管理器仍然保持可观察行为不变。
///
[TestFixture]
public class ArchitectureModulesBehaviorTests
@@ -57,7 +56,7 @@ public class ArchitectureModulesBehaviorTests
}
///
- /// 验证注册的 Mediator 行为会参与请求管道执行。
+ /// 验证注册的 CQRS 行为会参与请求管道执行。
///
[Test]
public async Task RegisterMediatorBehavior_Should_Apply_Pipeline_Behavior_To_Request()
@@ -83,12 +82,6 @@ public class ArchitectureModulesBehaviorTests
///
private sealed class ModuleTestArchitecture(Action registrationAction) : Architecture
{
- ///
- /// 打开 Mediator 服务注册,以便测试中介行为接入。
- ///
- public override Action? Configurator =>
- services => services.AddMediator(options => { options.ServiceLifetime = ServiceLifetime.Singleton; });
-
///
/// 在初始化阶段执行测试注入的模块注册逻辑。
///
@@ -136,14 +129,14 @@ public class ArchitectureModulesBehaviorTests
///
/// 用于验证管道行为注册是否生效的测试请求。
///
-public sealed class ModuleBehaviorRequest : IRequest
+public sealed class ModuleBehaviorRequest : GfCqrs.IRequest
{
}
///
/// 处理测试请求的处理器。
///
-public sealed class ModuleBehaviorRequestHandler : IRequestHandler
+public sealed class ModuleBehaviorRequestHandler : GfCqrs.IRequestHandler
{
///
/// 返回固定结果,便于聚焦验证管道行为是否执行。
@@ -162,8 +155,8 @@ public sealed class ModuleBehaviorRequestHandler : IRequestHandler
/// 请求类型。
/// 响应类型。
-public sealed class TrackingPipelineBehavior : IPipelineBehavior
- where TRequest : IRequest
+public sealed class TrackingPipelineBehavior : GfCqrs.IPipelineBehavior
+ where TRequest : GfCqrs.IRequest
{
///
/// 获取当前测试进程中该请求类型对应的行为触发次数。
@@ -178,11 +171,10 @@ public sealed class TrackingPipelineBehavior : IPipelineBeh
/// 取消令牌。
/// 下游处理器的响应结果。
public async ValueTask Handle(
- TRequest message,
- MessageHandlerDelegate next,
+ TRequest message, GfCqrs.MessageHandlerDelegate next,
CancellationToken cancellationToken)
{
InvocationCount++;
return await next(message, cancellationToken);
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
index 93244298..512e8a55 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
@@ -347,58 +347,61 @@ public class TestArchitectureContextV3 : IArchitectureContext
{
}
- public ValueTask SendRequestAsync(IRequest request,
+ public ValueTask SendRequestAsync(global::GFramework.Core.Abstractions.Cqrs.IRequest request,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
- public TResponse SendRequest(IRequest request)
+ public TResponse SendRequest(global::GFramework.Core.Abstractions.Cqrs.IRequest request)
{
throw new NotImplementedException();
}
- public ValueTask SendCommandAsync(global::Mediator.ICommand command,
+ public ValueTask SendCommandAsync(
+ global::GFramework.Core.Abstractions.Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
- public TResponse SendCommand(global::Mediator.ICommand command)
+ public TResponse SendCommand(global::GFramework.Core.Abstractions.Cqrs.Command.ICommand command)
{
throw new NotImplementedException();
}
- public ValueTask SendQueryAsync(global::Mediator.IQuery query,
+ public ValueTask SendQueryAsync(
+ global::GFramework.Core.Abstractions.Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
- public TResponse SendQuery(global::Mediator.IQuery query)
+ public TResponse SendQuery(global::GFramework.Core.Abstractions.Cqrs.Query.IQuery query)
{
throw new NotImplementedException();
}
public ValueTask PublishAsync(TNotification notification,
- CancellationToken cancellationToken = default) where TNotification : INotification
+ CancellationToken cancellationToken = default) where TNotification : global::GFramework.Core.Abstractions.Cqrs.INotification
{
throw new NotImplementedException();
}
- public IAsyncEnumerable CreateStream(IStreamRequest request,
+ public IAsyncEnumerable CreateStream(
+ global::GFramework.Core.Abstractions.Cqrs.IStreamRequest request,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default)
- where TCommand : IRequest
+ where TCommand : global::GFramework.Core.Abstractions.Cqrs.IRequest
{
throw new NotImplementedException();
}
- public ValueTask SendAsync(IRequest command,
+ public ValueTask SendAsync(global::GFramework.Core.Abstractions.Cqrs.IRequest command,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
@@ -439,4 +442,4 @@ public class TestArchitectureContextV3 : IArchitectureContext
}
}
-#endregion
\ No newline at end of file
+#endregion
diff --git a/GFramework.Core.Tests/Architectures/GameContextTests.cs b/GFramework.Core.Tests/Architectures/GameContextTests.cs
index 8f1bea57..6c2670bc 100644
--- a/GFramework.Core.Tests/Architectures/GameContextTests.cs
+++ b/GFramework.Core.Tests/Architectures/GameContextTests.cs
@@ -394,58 +394,61 @@ public class TestArchitectureContext : IArchitectureContext
{
}
- public ValueTask SendRequestAsync(IRequest request,
+ public ValueTask SendRequestAsync(global::GFramework.Core.Abstractions.Cqrs.IRequest request,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
- public TResponse SendRequest(IRequest request)
+ public TResponse SendRequest(global::GFramework.Core.Abstractions.Cqrs.IRequest request)
{
throw new NotImplementedException();
}
- public ValueTask SendCommandAsync(global::Mediator.ICommand command,
+ public ValueTask SendCommandAsync(
+ global::GFramework.Core.Abstractions.Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
- public TResponse SendCommand(global::Mediator.ICommand command)
+ public TResponse SendCommand(global::GFramework.Core.Abstractions.Cqrs.Command.ICommand command)
{
throw new NotImplementedException();
}
- public ValueTask SendQueryAsync(global::Mediator.IQuery query,
+ public ValueTask SendQueryAsync(
+ global::GFramework.Core.Abstractions.Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
- public TResponse SendQuery(global::Mediator.IQuery query)
+ public TResponse SendQuery(global::GFramework.Core.Abstractions.Cqrs.Query.IQuery query)
{
throw new NotImplementedException();
}
public ValueTask PublishAsync(TNotification notification,
- CancellationToken cancellationToken = default) where TNotification : INotification
+ CancellationToken cancellationToken = default) where TNotification : global::GFramework.Core.Abstractions.Cqrs.INotification
{
throw new NotImplementedException();
}
- public IAsyncEnumerable CreateStream(IStreamRequest request,
+ public IAsyncEnumerable CreateStream(
+ global::GFramework.Core.Abstractions.Cqrs.IStreamRequest request,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
public ValueTask SendAsync(TCommand command, CancellationToken cancellationToken = default)
- where TCommand : IRequest
+ where TCommand : global::GFramework.Core.Abstractions.Cqrs.IRequest
{
throw new NotImplementedException();
}
- public ValueTask SendAsync(IRequest command,
+ public ValueTask SendAsync(global::GFramework.Core.Abstractions.Cqrs.IRequest command,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
@@ -510,4 +513,4 @@ public class TestArchitectureContext : IArchitectureContext
{
return Environment;
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Tests/Cqrs/CqrsHandlerRegistrarTests.cs b/GFramework.Core.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
new file mode 100644
index 00000000..28b1fb32
--- /dev/null
+++ b/GFramework.Core.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
@@ -0,0 +1,221 @@
+using System.Reflection;
+using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Core.Abstractions.Logging;
+using GFramework.Core.Architectures;
+using GFramework.Core.Ioc;
+using GFramework.Core.Logging;
+using GFramework.Core.Tests.Logging;
+
+namespace GFramework.Core.Tests.Cqrs;
+
+///
+/// 验证 CQRS 处理器自动注册在顺序与容错层面的可观察行为。
+///
+[TestFixture]
+internal sealed class CqrsHandlerRegistrarTests
+{
+ private MicrosoftDiContainer? _container;
+ private ArchitectureContext? _context;
+
+ ///
+ /// 初始化测试容器并重置共享状态。
+ ///
+ [SetUp]
+ public void SetUp()
+ {
+ LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
+ DeterministicNotificationHandlerState.Reset();
+
+ _container = new MicrosoftDiContainer();
+ CqrsTestRuntime.RegisterHandlers(
+ _container,
+ typeof(CqrsHandlerRegistrarTests).Assembly);
+
+ _container.Freeze();
+ _context = new ArchitectureContext(_container);
+ }
+
+ ///
+ /// 清理测试过程中创建的上下文与共享状态。
+ ///
+ [TearDown]
+ public void TearDown()
+ {
+ _context = null;
+ _container = null;
+ DeterministicNotificationHandlerState.Reset();
+ }
+
+ ///
+ /// 验证自动扫描到的通知处理器会按稳定名称顺序执行,而不是依赖反射枚举顺序。
+ ///
+ [Test]
+ public async Task PublishAsync_Should_Run_Notification_Handlers_In_Deterministic_Name_Order()
+ {
+ await _context!.PublishAsync(new DeterministicOrderNotification());
+
+ Assert.That(
+ DeterministicNotificationHandlerState.InvocationOrder,
+ Is.EqualTo(
+ [
+ nameof(AlphaDeterministicNotificationHandler),
+ nameof(ZetaDeterministicNotificationHandler)
+ ]));
+ }
+
+ ///
+ /// 验证部分类型加载失败时仍能保留可加载类型,并记录诊断日志。
+ ///
+ [Test]
+ public void RegisterHandlers_Should_Register_Loadable_Types_And_Log_Warnings_When_Assembly_Load_Partially_Fails()
+ {
+ var originalProvider = LoggerFactoryResolver.Provider;
+ var capturingProvider = new CapturingLoggerFactoryProvider(LogLevel.Warning);
+ var reflectionTypeLoadException = new ReflectionTypeLoadException(
+ [typeof(AlphaDeterministicNotificationHandler), null],
+ [new TypeLoadException("Missing optional dependency for registrar test.")]);
+ var partiallyLoadableAssembly = new Mock();
+ partiallyLoadableAssembly
+ .SetupGet(static assembly => assembly.FullName)
+ .Returns("GFramework.Core.Tests.Cqrs.PartiallyLoadableAssembly, Version=1.0.0.0");
+ partiallyLoadableAssembly
+ .Setup(static assembly => assembly.GetTypes())
+ .Throws(reflectionTypeLoadException);
+
+ LoggerFactoryResolver.Provider = capturingProvider;
+ try
+ {
+ var container = new MicrosoftDiContainer();
+ CqrsTestRuntime.RegisterHandlers(container, partiallyLoadableAssembly.Object);
+ container.Freeze();
+
+ var handlers = container.GetAll>();
+ var warningLogs = capturingProvider.Loggers
+ .SelectMany(static logger => logger.Logs)
+ .Where(static log => log.Level == LogLevel.Warning)
+ .ToList();
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(
+ handlers.Select(static handler => handler.GetType()),
+ Is.EqualTo([typeof(AlphaDeterministicNotificationHandler)]));
+ Assert.That(warningLogs.Count, Is.GreaterThanOrEqualTo(2));
+ Assert.That(
+ warningLogs.Any(log => log.Message.Contains("partially failed", StringComparison.Ordinal)),
+ Is.True);
+ Assert.That(
+ warningLogs.Any(log =>
+ log.Message.Contains("Missing optional dependency", StringComparison.Ordinal)),
+ Is.True);
+ });
+ }
+ finally
+ {
+ LoggerFactoryResolver.Provider = originalProvider;
+ }
+ }
+}
+
+///
+/// 记录确定性通知处理器的实际执行顺序。
+///
+internal static class DeterministicNotificationHandlerState
+{
+ ///
+ /// 获取当前测试中的通知处理器执行顺序。
+ ///
+ public static List InvocationOrder { get; } = [];
+
+ ///
+ /// 重置共享的执行顺序状态。
+ ///
+ public static void Reset()
+ {
+ InvocationOrder.Clear();
+ }
+}
+
+///
+/// 用于验证同一通知的多个处理器是否按稳定顺序执行。
+///
+internal sealed record DeterministicOrderNotification : INotification;
+
+///
+/// 故意放在 Alpha 之前声明,用于验证注册器不会依赖源码声明顺序。
+///
+internal sealed class ZetaDeterministicNotificationHandler : INotificationHandler
+{
+ ///
+ /// 记录当前处理器已执行。
+ ///
+ /// 通知实例。
+ /// 取消令牌。
+ /// 已完成任务。
+ public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken)
+ {
+ DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(ZetaDeterministicNotificationHandler));
+ return ValueTask.CompletedTask;
+ }
+}
+
+///
+/// 名称排序上应先于 Zeta 处理器执行的通知处理器。
+///
+internal sealed class AlphaDeterministicNotificationHandler : INotificationHandler
+{
+ ///
+ /// 记录当前处理器已执行。
+ ///
+ /// 通知实例。
+ /// 取消令牌。
+ /// 已完成任务。
+ public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken)
+ {
+ DeterministicNotificationHandlerState.InvocationOrder.Add(nameof(AlphaDeterministicNotificationHandler));
+ return ValueTask.CompletedTask;
+ }
+}
+
+///
+/// 为 CQRS 注册测试捕获真实启动路径中创建的日志记录器。
+///
+///
+/// 处理器注册入口会分别为测试运行时、容器和注册器创建日志器。
+/// 该提供程序统一保留这些测试日志器,以便断言警告是否经由公开入口真正发出。
+///
+internal sealed class CapturingLoggerFactoryProvider : ILoggerFactoryProvider
+{
+ private readonly List _loggers = [];
+
+ ///
+ /// 使用指定的最小日志级别初始化一个新的捕获型日志工厂提供程序。
+ ///
+ /// 要应用到新建测试日志器的最小日志级别。
+ public CapturingLoggerFactoryProvider(LogLevel minLevel = LogLevel.Info)
+ {
+ MinLevel = minLevel;
+ }
+
+ ///
+ /// 获取通过当前提供程序创建的全部测试日志器。
+ ///
+ public IReadOnlyList Loggers => _loggers;
+
+ ///
+ /// 获取或设置新建测试日志器的最小日志级别。
+ ///
+ public LogLevel MinLevel { get; set; }
+
+ ///
+ /// 创建一个测试日志器并将其纳入捕获集合。
+ ///
+ /// 日志记录器名称。
+ /// 用于后续断言的测试日志器。
+ public ILogger CreateLogger(string name)
+ {
+ var logger = new TestLogger(name, MinLevel);
+ _loggers.Add(logger);
+ return logger;
+ }
+}
diff --git a/GFramework.Core.Tests/CqrsTestRuntime.cs b/GFramework.Core.Tests/CqrsTestRuntime.cs
new file mode 100644
index 00000000..e9664925
--- /dev/null
+++ b/GFramework.Core.Tests/CqrsTestRuntime.cs
@@ -0,0 +1,56 @@
+using System.Reflection;
+using GFramework.Core.Abstractions.Ioc;
+using GFramework.Core.Abstractions.Logging;
+using GFramework.Core.Architectures;
+using GFramework.Core.Ioc;
+using GFramework.Core.Logging;
+
+namespace GFramework.Core.Tests;
+
+///
+/// 为测试项目提供对 CQRS 处理器真实注册入口的受控访问。
+///
+///
+/// 测试应通过该入口驱动注册流程,而不是直接反射调用注册器的私有辅助方法,
+/// 这样可以覆盖生产启动路径中的程序集去重、日志记录与容错恢复行为。
+///
+internal static class CqrsTestRuntime
+{
+ private static readonly Type CqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
+ .GetType(
+ "GFramework.Core.Cqrs.Internal.CqrsHandlerRegistrar",
+ throwOnError: true)!
+ ?? throw new InvalidOperationException(
+ "Failed to locate CqrsHandlerRegistrar type.");
+
+ private static readonly MethodInfo RegisterHandlersMethod = CqrsHandlerRegistrarType
+ .GetMethod(
+ "RegisterHandlers",
+ BindingFlags.Public | BindingFlags.NonPublic |
+ BindingFlags.Static,
+ binder: null,
+ [
+ typeof(IIocContainer),
+ typeof(IEnumerable),
+ typeof(ILogger)
+ ],
+ modifiers: null)
+ ?? throw new InvalidOperationException(
+ "Failed to locate CqrsHandlerRegistrar.RegisterHandlers.");
+
+ ///
+ /// 通过与生产代码一致的注册入口扫描并注册指定程序集中的 CQRS 处理器。
+ ///
+ /// 承载处理器映射的测试容器。
+ /// 要扫描的程序集集合。
+ internal static void RegisterHandlers(MicrosoftDiContainer container, params Assembly[] assemblies)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+ ArgumentNullException.ThrowIfNull(assemblies);
+
+ var logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
+ RegisterHandlersMethod.Invoke(
+ null,
+ [container, assemblies.Where(static assembly => assembly is not null).Distinct().ToArray(), logger]);
+ }
+}
diff --git a/GFramework.Core.Tests/Mediator/MediatorAdvancedFeaturesTests.cs b/GFramework.Core.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
index bc5cd782..2dc2503a 100644
--- a/GFramework.Core.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
+++ b/GFramework.Core.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
@@ -1,10 +1,9 @@
using System.Diagnostics;
using System.Reflection;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Architectures;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
-using Mediator;
-using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Tests.Mediator;
@@ -15,22 +14,26 @@ namespace GFramework.Core.Tests.Mediator;
[TestFixture]
public class MediatorAdvancedFeaturesTests
{
+ private MicrosoftDiContainer? _container;
+
+ private ArchitectureContext? _context;
+
[SetUp]
public void SetUp()
{
LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
_container = new MicrosoftDiContainer();
+ TestCircuitBreakerHandler.Reset();
var loggerField = typeof(MicrosoftDiContainer).GetField("_logger",
BindingFlags.NonPublic | BindingFlags.Instance);
loggerField?.SetValue(_container,
LoggerFactoryResolver.Provider.CreateLogger(nameof(MediatorAdvancedFeaturesTests)));
- // 注册Mediator及相关处理器
- _container.ExecuteServicesHook(configurator =>
- {
- configurator.AddMediator(options => { options.ServiceLifetime = ServiceLifetime.Singleton; });
- });
+ CqrsTestRuntime.RegisterHandlers(
+ _container,
+ typeof(MediatorAdvancedFeaturesTests).Assembly,
+ typeof(ArchitectureContext).Assembly);
_container.Freeze();
_context = new ArchitectureContext(_container);
@@ -43,9 +46,6 @@ public class MediatorAdvancedFeaturesTests
_container = null;
}
- private ArchitectureContext? _context;
- private MicrosoftDiContainer? _container;
-
[Test]
public async Task Request_With_Validation_Behavior_Should_Validate_Input()
@@ -136,9 +136,6 @@ public class MediatorAdvancedFeaturesTests
[Test]
public async Task Circuit_Breaker_Should_Prevent_Cascading_Failures()
{
- TestCircuitBreakerHandler.FailureCount = 0;
- TestCircuitBreakerHandler.SuccessCount = 0;
-
// 先触发几次失败
for (int i = 0; i < 5; i++)
{
@@ -276,12 +273,10 @@ public sealed class TestTransientErrorRequestHandler : IRequestHandler
{
- private static bool _circuitOpen = false;
-
public ValueTask Handle(TestCircuitBreakerRequest request, CancellationToken cancellationToken)
{
// 检查断路器状态
- if (_circuitOpen)
+ if (TestCircuitBreakerHandler.CircuitOpen)
{
throw new InvalidOperationException("Circuit breaker is open");
}
@@ -293,7 +288,7 @@ public sealed class TestCircuitBreakerRequestHandler : IRequestHandler= 5)
{
- _circuitOpen = true;
+ TestCircuitBreakerHandler.CircuitOpen = true;
}
throw new InvalidOperationException("Service unavailable");
@@ -452,6 +447,17 @@ public static class TestCircuitBreakerHandler
{
public static int FailureCount { get; set; }
public static int SuccessCount { get; set; }
+ public static bool CircuitOpen { get; set; }
+
+ ///
+ /// 重置断路器测试状态,避免静态字段在测试之间互相污染。
+ ///
+ public static void Reset()
+ {
+ FailureCount = 0;
+ SuccessCount = 0;
+ CircuitOpen = false;
+ }
}
public sealed record TestCircuitBreakerRequest : IRequest
@@ -487,4 +493,4 @@ public sealed record TestDatabaseRequest : IRequest
public List Storage { get; init; } = new();
}
-#endregion
\ No newline at end of file
+#endregion
diff --git a/GFramework.Core.Tests/Mediator/MediatorArchitectureIntegrationTests.cs b/GFramework.Core.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
index 85bbf3aa..e176cce5 100644
--- a/GFramework.Core.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
+++ b/GFramework.Core.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
@@ -1,12 +1,12 @@
using System.Diagnostics;
using System.Reflection;
using GFramework.Core.Abstractions.Architectures;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Architectures;
using GFramework.Core.Command;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
-using Mediator;
-using Microsoft.Extensions.DependencyInjection;
+using GFramework.Core.Rule;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Tests.Mediator;
@@ -18,11 +18,17 @@ namespace GFramework.Core.Tests.Mediator;
[TestFixture]
public class MediatorArchitectureIntegrationTests
{
+ private CommandExecutor? _commandBus;
+ private MicrosoftDiContainer? _container;
+
+ private ArchitectureContext? _context;
+
[SetUp]
public void SetUp()
{
LoggerFactoryResolver.Provider = new ConsoleLoggerFactoryProvider();
_container = new MicrosoftDiContainer();
+ TestPerDispatchContextAwareHandler.Reset();
var loggerField = typeof(MicrosoftDiContainer).GetField("_logger",
BindingFlags.NonPublic | BindingFlags.Instance);
@@ -33,11 +39,10 @@ public class MediatorArchitectureIntegrationTests
_commandBus = new CommandExecutor();
_container.RegisterPlurality(_commandBus);
- // 注册Mediator
- _container.ExecuteServicesHook(configurator =>
- {
- configurator.AddMediator(options => { options.ServiceLifetime = ServiceLifetime.Singleton; });
- });
+ CqrsTestRuntime.RegisterHandlers(
+ _container,
+ typeof(MediatorArchitectureIntegrationTests).Assembly,
+ typeof(ArchitectureContext).Assembly);
_container.Freeze();
_context = new ArchitectureContext(_container);
@@ -51,10 +56,6 @@ public class MediatorArchitectureIntegrationTests
_commandBus = null;
}
- private ArchitectureContext? _context;
- private MicrosoftDiContainer? _container;
- private CommandExecutor? _commandBus;
-
[Test]
public async Task Handler_Can_Access_Architecture_Context()
{
@@ -292,6 +293,20 @@ public class MediatorArchitectureIntegrationTests
Assert.That(traditionalCommand.Executed, Is.True);
Assert.That(result, Is.EqualTo(42));
}
+
+ [Test]
+ public async Task ContextAware_Handler_Should_Use_A_Fresh_Instance_Per_Request()
+ {
+ var firstResult = await _context!.SendRequestAsync(new TestPerDispatchContextAwareRequest());
+ var secondResult = await _context.SendRequestAsync(new TestPerDispatchContextAwareRequest());
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(firstResult, Is.Not.EqualTo(secondResult));
+ Assert.That(TestPerDispatchContextAwareHandler.SeenInstanceIds, Is.EqualTo([firstResult, secondResult]));
+ Assert.That(TestPerDispatchContextAwareHandler.Contexts, Has.All.SameAs(_context));
+ });
+ }
}
#region Integration Test Classes
@@ -445,6 +460,42 @@ public sealed class TestMediatorRequestHandler : IRequestHandler
+/// 用于验证自动扫描到的上下文感知处理器会按请求创建新实例。
+///
+public sealed class TestPerDispatchContextAwareHandler : ContextAwareBase,
+ IRequestHandler
+{
+ private static int _nextInstanceId;
+ private readonly int _instanceId = Interlocked.Increment(ref _nextInstanceId);
+
+ public static List Contexts { get; } = [];
+ public static List SeenInstanceIds { get; } = [];
+
+ ///
+ /// 记录当前实例编号与收到的架构上下文。
+ ///
+ /// 请求实例。
+ /// 取消令牌。
+ /// 当前处理器实例编号。
+ public ValueTask Handle(TestPerDispatchContextAwareRequest request, CancellationToken cancellationToken)
+ {
+ Contexts.Add(Context);
+ SeenInstanceIds.Add(_instanceId);
+ return ValueTask.FromResult(_instanceId);
+ }
+
+ ///
+ /// 重置跨测试共享的实例跟踪状态。
+ ///
+ public static void Reset()
+ {
+ Contexts.Clear();
+ SeenInstanceIds.Clear();
+ _nextInstanceId = 0;
+ }
+}
+
public sealed record TestContextAwareRequest : IRequest;
public static class TestContextAwareHandler
@@ -545,6 +596,11 @@ public sealed record TestMediatorRequest : IRequest
public int Value { get; init; }
}
+///
+/// 用于验证每次请求分发都会获得新的上下文感知处理器实例。
+///
+public sealed record TestPerDispatchContextAwareRequest : IRequest;
+
// 传统命令用于混合测试
public class TestTraditionalCommand : ICommand
{
@@ -559,4 +615,4 @@ public class TestTraditionalCommand : ICommand
public IArchitectureContext GetContext() => null!;
}
-#endregion
\ No newline at end of file
+#endregion
diff --git a/GFramework.Core.Tests/Mediator/MediatorComprehensiveTests.cs b/GFramework.Core.Tests/Mediator/MediatorComprehensiveTests.cs
index 020bc26b..27dfed5c 100644
--- a/GFramework.Core.Tests/Mediator/MediatorComprehensiveTests.cs
+++ b/GFramework.Core.Tests/Mediator/MediatorComprehensiveTests.cs
@@ -2,6 +2,7 @@ using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using GFramework.Core.Abstractions.Architectures;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Events;
using GFramework.Core.Architectures;
using GFramework.Core.Command;
@@ -10,22 +11,26 @@ using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Query;
-using Mediator;
-using Microsoft.Extensions.DependencyInjection;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
-
-// ✅ Mediator 库的命名空间
-
-// ✅ 使用 global using 或别名来区分
+using Unit = GFramework.Core.Abstractions.Cqrs.Unit;
namespace GFramework.Core.Tests.Mediator;
[TestFixture]
public class MediatorComprehensiveTests
{
+ private AsyncQueryExecutor? _asyncQueryBus;
+ private CommandExecutor? _commandBus;
+ private MicrosoftDiContainer? _container;
+
+ private ArchitectureContext? _context;
+ private DefaultEnvironment? _environment;
+ private EventBus? _eventBus;
+ private QueryExecutor? _queryBus;
+
///
/// 测试初始化方法,在每个测试方法执行前运行。
- /// 负责初始化日志工厂、依赖注入容器、Mediator以及各种总线服务。
+ /// 负责初始化日志工厂、依赖注入容器、自有 CQRS 处理器以及各种总线服务。
///
[SetUp]
public void SetUp()
@@ -51,13 +56,11 @@ public class MediatorComprehensiveTests
_container.RegisterPlurality(_asyncQueryBus);
_container.RegisterPlurality(_environment);
- // ✅ 注册 Mediator
- _container.ExecuteServicesHook(configurator =>
- {
- configurator.AddMediator(options => { options.ServiceLifetime = ServiceLifetime.Singleton; });
- });
+ CqrsTestRuntime.RegisterHandlers(
+ _container,
+ typeof(MediatorComprehensiveTests).Assembly,
+ typeof(ArchitectureContext).Assembly);
- // ✅ Freeze 容器
_container.Freeze();
_context = new ArchitectureContext(_container);
@@ -79,14 +82,6 @@ public class MediatorComprehensiveTests
_environment = null;
}
- private ArchitectureContext? _context;
- private MicrosoftDiContainer? _container;
- private EventBus? _eventBus;
- private CommandExecutor? _commandBus;
- private QueryExecutor? _queryBus;
- private AsyncQueryExecutor? _asyncQueryBus;
- private DefaultEnvironment? _environment;
-
///
/// 测试SendRequestAsync方法在请求有效时返回结果
///
@@ -194,19 +189,19 @@ public class MediatorComprehensiveTests
///
- /// 测试未注册的Mediator抛出InvalidOperationException
+ /// 测试未注册的 CQRS handler 时抛出 InvalidOperationException
///
[Test]
- public void Unregistered_Mediator_Should_Throw_InvalidOperationException()
+ public void Unregistered_Cqrs_Handler_Should_Throw_InvalidOperationException()
{
- var containerWithoutMediator = new MicrosoftDiContainer();
- containerWithoutMediator.Freeze();
+ var containerWithoutHandlers = new MicrosoftDiContainer();
+ containerWithoutHandlers.Freeze();
- var contextWithoutMediator = new ArchitectureContext(containerWithoutMediator);
+ var contextWithoutHandlers = new ArchitectureContext(containerWithoutHandlers);
var testRequest = new TestRequest { Value = 42 };
Assert.ThrowsAsync(async () =>
- await contextWithoutMediator.SendRequestAsync(testRequest));
+ await contextWithoutHandlers.SendRequestAsync(testRequest));
}
///
@@ -270,10 +265,10 @@ public class MediatorComprehensiveTests
}
///
- /// 测试并发Mediator请求不会相互干扰
+ /// 测试并发 CQRS 请求不会相互干扰
///
[Test]
- public async Task Concurrent_Mediator_Requests_Should_Not_Interfere()
+ public async Task Concurrent_Cqrs_Requests_Should_Not_Interfere()
{
const int requestCount = 10;
var tasks = new List>();
@@ -389,10 +384,10 @@ public class MediatorComprehensiveTests
}
///
- /// 测试Mediator性能基准
+ /// 测试 CQRS 性能基准
///
[Test]
- public async Task Performance_Benchmark_For_Mediator()
+ public async Task Performance_Benchmark_For_Cqrs()
{
const int iterations = 1000;
var stopwatch = Stopwatch.StartNew();
@@ -413,17 +408,17 @@ public class MediatorComprehensiveTests
}
///
- /// 测试Mediator和传统CQRS可以共存
+ /// 测试自有 CQRS 和传统 CQRS 可以共存
///
[Test]
- public async Task Mediator_And_Legacy_CQRS_Can_Coexist()
+ public async Task Cqrs_And_Legacy_CQRS_Can_Coexist()
{
// 使用传统方式
var legacyCommand = new TestLegacyCommand();
_context!.SendCommand(legacyCommand);
Assert.That(legacyCommand.Executed, Is.True);
- // 使用Mediator方式
+ // 使用自有 CQRS 方式
var mediatorCommand = new TestCommandWithResult { ResultValue = 999 };
var result = await _context.SendAsync(mediatorCommand);
Assert.That(result, Is.EqualTo(999));
@@ -434,7 +429,7 @@ public class MediatorComprehensiveTests
}
}
-#region Advanced Test Classes for Mediator Features
+#region Advanced Test Classes for CQRS Features
public sealed record TestLongRunningRequest : IRequest
{
@@ -628,9 +623,9 @@ public class TestLegacyCommand : ICommand
#endregion
-#region Test Classes - Mediator (新实现)
+#region Test Classes - CQRS Runtime
-// ✅ 这些类使用 Mediator.IRequest
+// ✅ 这些类使用自有 CQRS IRequest
public sealed record TestRequest : IRequest
{
public int Value { get; init; }
@@ -662,7 +657,7 @@ public sealed record TestStreamRequest : IStreamRequest
public int[] Values { get; init; } = [];
}
-// ✅ 这些 Handler 使用 Mediator.IRequestHandler
+// ✅ 这些 Handler 使用自有 CQRS IRequestHandler
public sealed class TestRequestHandler : IRequestHandler
{
public ValueTask Handle(TestRequest request, CancellationToken cancellationToken)
@@ -726,4 +721,4 @@ public sealed class TestStreamRequestHandler : IStreamRequestHandler
/// 为服务容器设置上下文并执行扩展配置钩子。
- /// 这一步统一承接 Mediator 等容器扩展的接入点,避免 直接操作容器细节。
+ /// 这一步统一承接 CQRS 运行时与容器扩展的接入点,避免 直接操作容器细节。
///
/// 当前架构上下文。
/// 可选的服务集合配置委托。
private void ConfigureServices(IArchitectureContext context, Action? configurator)
{
services.SetContext(context);
+ CqrsHandlerRegistrar.RegisterHandlers(
+ services.Container,
+ [architectureType.Assembly, typeof(ArchitectureContext).Assembly],
+ logger);
if (configurator is null)
- logger.Debug("Mediator-based cqrs will not take effect without the service setter configured!");
+ logger.Debug("No external service configurator provided. Using built-in CQRS runtime registration only.");
services.Container.ExecuteServicesHook(configurator);
}
@@ -115,4 +120,4 @@ internal sealed class ArchitectureBootstrapper(
{
await services.ModuleManager.InitializeAllAsync(asyncMode);
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Architectures/ArchitectureContext.cs b/GFramework.Core/Architectures/ArchitectureContext.cs
index 1e3d72a7..77c04fcf 100644
--- a/GFramework.Core/Architectures/ArchitectureContext.cs
+++ b/GFramework.Core/Architectures/ArchitectureContext.cs
@@ -1,14 +1,19 @@
using System.Collections.Concurrent;
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Command;
+using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Core.Abstractions.Cqrs.Command;
+using GFramework.Core.Abstractions.Cqrs.Query;
using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Abstractions.Events;
using GFramework.Core.Abstractions.Ioc;
+using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Query;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility;
-using Mediator;
+using GFramework.Core.Cqrs.Internal;
+using GFramework.Core.Logging;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Architectures;
@@ -20,23 +25,15 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
{
private readonly IIocContainer _container = container ?? throw new ArgumentNullException(nameof(container));
private readonly ConcurrentDictionary _serviceCache = new();
+ private readonly ILogger _logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(ArchitectureContext));
+ private CqrsDispatcher? _cqrsDispatcher;
- #region Mediator Integration
+ #region CQRS Integration
///
- /// 获取 Mediator 实例(延迟加载)
+ /// 获取 CQRS 运行时分发器(延迟初始化)。
///
- private IMediator Mediator => GetOrCache();
-
- ///
- /// 获取 ISender 实例(更轻量的发送器)
- ///
- private ISender Sender => GetOrCache();
-
- ///
- /// 获取 IPublisher 实例(用于发布通知)
- ///
- private IPublisher Publisher => GetOrCache();
+ private CqrsDispatcher CqrsDispatcher => _cqrsDispatcher ??= new CqrsDispatcher(_container, this, _logger);
///
/// 获取指定类型的服务实例,如果缓存中存在则直接返回,否则从容器中获取并缓存
@@ -64,30 +61,23 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
}
///
- /// [Mediator] 发送请求(Command/Query)
- /// 这是推荐的新方式,统一处理命令和查询
+ /// 发送请求(Command/Query)
+ /// 使用 GFramework 自有 CQRS runtime 统一处理命令和查询。
///
/// 响应类型
/// 请求对象(Command 或 Query)
/// 取消令牌
/// 响应结果
- /// 当 Mediator 未注册时抛出
public async ValueTask SendRequestAsync(
IRequest request,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
-
- var mediator = Mediator;
- if (mediator == null)
- throw new InvalidOperationException(
- "Mediator not registered. Call EnableMediator() in your Architecture.OnInitialize() method.");
-
- return await mediator.Send(request, cancellationToken);
+ return await CqrsDispatcher.SendAsync(request, cancellationToken);
}
///
- /// [Mediator] 发送请求的同步版本(不推荐,仅用于兼容性)
+ /// 发送请求的同步版本(不推荐,仅用于兼容性)
///
/// 响应类型
/// 请求对象
@@ -98,8 +88,8 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
}
///
- /// [Mediator] 发布通知(一对多)
- /// 用于事件驱动场景,多个处理器可以同时处理同一个通知
+ /// 发布通知(一对多)
+ /// 使用 GFramework 自有 CQRS runtime 分发到所有已注册通知处理器。
///
/// 通知类型
/// 通知对象
@@ -110,16 +100,11 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
where TNotification : INotification
{
ArgumentNullException.ThrowIfNull(notification);
-
- var publisher = Publisher;
- if (publisher == null)
- throw new InvalidOperationException("Publisher not registered.");
-
- await publisher.Publish(notification, cancellationToken);
+ await CqrsDispatcher.PublishAsync(notification, cancellationToken);
}
///
- /// [Mediator] 发送请求并返回流(用于大数据集)
+ /// 发送请求并返回流(用于大数据集)
///
/// 响应项类型
/// 流式请求
@@ -130,12 +115,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
-
- var mediator = Mediator;
- if (mediator == null)
- throw new InvalidOperationException("Mediator not registered.");
-
- return mediator.CreateStream(request, cancellationToken);
+ return CqrsDispatcher.CreateStream(request, cancellationToken);
}
///
@@ -180,12 +160,12 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
}
///
- /// [Mediator] 发送查询的同步版本(不推荐,仅用于兼容性)
+ /// 发送 CQRS 查询的同步版本(不推荐,仅用于兼容性)
///
/// 查询响应类型
/// 要发送的查询对象
/// 查询结果
- public TResponse SendQuery(Mediator.IQuery query)
+ public TResponse SendQuery(GFramework.Core.Abstractions.Cqrs.Query.IQuery query)
{
return SendQueryAsync(query).AsTask().GetAwaiter().GetResult();
}
@@ -205,23 +185,17 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
}
///
- /// [Mediator] 异步发送查询并返回结果
- /// 通过Mediator模式发送查询请求,支持取消操作
+ /// 异步发送 CQRS 查询并返回结果。
///
/// 查询响应类型
/// 要发送的查询对象
/// 取消令牌,用于取消操作
/// 包含查询结果的ValueTask
- public async ValueTask SendQueryAsync(Mediator.IQuery query,
+ public async ValueTask SendQueryAsync(GFramework.Core.Abstractions.Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(query);
-
- var sender = Sender;
- if (sender == null)
- throw new InvalidOperationException("Sender not registered.");
-
- return await sender.Send(query, cancellationToken);
+ return await SendRequestAsync(query, cancellationToken);
}
#endregion
@@ -347,23 +321,17 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
#region Command Execution
///
- /// [Mediator] 异步发送命令并返回结果
- /// 通过Mediator模式发送命令请求,支持取消操作
+ /// 异步发送 CQRS 命令并返回结果。
///
/// 命令响应类型
/// 要发送的命令对象
/// 取消令牌,用于取消操作
/// 包含命令执行结果的ValueTask
- public async ValueTask SendCommandAsync(Mediator.ICommand command,
+ public async ValueTask SendCommandAsync(GFramework.Core.Abstractions.Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(command);
-
- var sender = Sender;
- if (sender == null)
- throw new InvalidOperationException("Sender not registered.");
-
- return await sender.Send(command, cancellationToken);
+ return await SendRequestAsync(command, cancellationToken);
}
///
@@ -393,12 +361,12 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
}
///
- /// [Mediator] 发送命令的同步版本(不推荐,仅用于兼容性)
+ /// 发送 CQRS 命令的同步版本(不推荐,仅用于兼容性)
///
/// 命令响应类型
/// 要发送的命令对象
/// 命令执行结果
- public TResponse SendCommand(Mediator.ICommand command)
+ public TResponse SendCommand(GFramework.Core.Abstractions.Cqrs.Command.ICommand command)
{
return SendCommandAsync(command).AsTask().GetAwaiter().GetResult();
}
@@ -491,4 +459,4 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
}
#endregion
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Command/AbstractCommandWithInput.cs b/GFramework.Core/Command/AbstractCommandWithInput.cs
index 5326fe6e..7512c760 100644
--- a/GFramework.Core/Command/AbstractCommandWithInput.cs
+++ b/GFramework.Core/Command/AbstractCommandWithInput.cs
@@ -9,13 +9,13 @@ namespace GFramework.Core.Command;
///
/// 命令输入参数类型,必须实现 ICommandInput 接口
/// 命令执行所需的输入参数
-public abstract class AbstractCommand(TInput input) : ContextAwareBase, ICommand
+public abstract class AbstractCommand(TInput input) : ContextAwareBase, GFramework.Core.Abstractions.Command.ICommand
where TInput : ICommandInput
{
///
/// 执行命令的入口方法,实现 ICommand 接口的 Execute 方法
///
- void ICommand.Execute()
+ void GFramework.Core.Abstractions.Command.ICommand.Execute()
{
OnExecute(input);
}
@@ -25,4 +25,4 @@ public abstract class AbstractCommand(TInput input) : ContextAwareBase,
///
/// 命令执行所需的输入参数
protected abstract void OnExecute(TInput input);
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Command/AbstractCommandWithResult.cs b/GFramework.Core/Command/AbstractCommandWithResult.cs
index 67901821..7ecc9522 100644
--- a/GFramework.Core/Command/AbstractCommandWithResult.cs
+++ b/GFramework.Core/Command/AbstractCommandWithResult.cs
@@ -10,14 +10,14 @@ namespace GFramework.Core.Command;
/// 命令输入参数类型,必须实现 ICommandInput 接口
/// 命令执行后返回的结果类型
/// 命令执行所需的输入参数
-public abstract class AbstractCommand(TInput input) : ContextAwareBase, ICommand
+public abstract class AbstractCommand(TInput input) : ContextAwareBase, GFramework.Core.Abstractions.Command.ICommand
where TInput : ICommandInput
{
///
/// 执行命令的入口方法,实现 ICommand{TResult} 接口的 Execute 方法
///
/// 命令执行后的结果
- TResult ICommand.Execute()
+ TResult GFramework.Core.Abstractions.Command.ICommand.Execute()
{
return OnExecute(input);
}
@@ -28,4 +28,4 @@ public abstract class AbstractCommand(TInput input) : ContextAw
/// 命令执行所需的输入参数
/// 命令执行后的结果
protected abstract TResult OnExecute(TInput input);
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs b/GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs
index 4a56c775..4aaf797a 100644
--- a/GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs
+++ b/GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs
@@ -12,9 +12,9 @@
// limitations under the License.
using System.Diagnostics;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Logging;
-using Mediator;
namespace GFramework.Core.Cqrs.Behaviors;
@@ -69,4 +69,4 @@ public sealed class LoggingBehavior : IPipelineBehavior : IPipelineBehavior
}
}
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs b/GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs
index 8f7b6112..528de106 100644
--- a/GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs
+++ b/GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs
@@ -11,17 +11,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Core.Abstractions.Cqrs.Command;
using GFramework.Core.Rule;
-using Mediator;
namespace GFramework.Core.Cqrs.Command;
///
/// 抽象命令处理器基类
-/// 继承自ContextAwareBase并实现ICommandHandler接口,为具体的命令处理器提供基础功能
+/// 继承自 ContextAwareBase 并实现 IRequestHandler 接口,为具体的命令处理器提供基础功能。
+/// 框架会在每次分发前注入当前架构上下文,因此派生类可以通过 Context 访问架构级服务。
///
/// 命令类型
-public abstract class AbstractCommandHandler : ContextAwareBase, ICommandHandler
+public abstract class AbstractCommandHandler : ContextAwareBase, IRequestHandler
where TCommand : ICommand
{
///
@@ -36,12 +38,12 @@ public abstract class AbstractCommandHandler : ContextAwareBase, IComm
///
/// 抽象命令处理器基类(带返回值版本)
-/// 继承自ContextAwareBase并实现ICommandHandler接口,为具体的命令处理器提供基础功能
-/// 支持泛型命令和结果类型,实现CQRS模式中的命令处理
+/// 继承自 ContextAwareBase 并实现 IRequestHandler 接口,为具体的命令处理器提供基础功能。
+/// 支持泛型命令和结果类型,框架会在每次分发前注入当前架构上下文。
///
/// 命令类型,必须实现ICommand接口
/// 命令执行结果类型
-public abstract class AbstractCommandHandler : ContextAwareBase, ICommandHandler
+public abstract class AbstractCommandHandler : ContextAwareBase, IRequestHandler
where TCommand : ICommand
{
///
@@ -52,4 +54,4 @@ public abstract class AbstractCommandHandler : ContextAwareBa
/// 取消令牌,用于取消操作
/// 表示异步操作完成的ValueTask,包含命令执行结果
public abstract ValueTask Handle(TCommand command, CancellationToken cancellationToken);
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs b/GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs
index 064cb279..847563c4 100644
--- a/GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs
+++ b/GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs
@@ -11,28 +11,37 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Core.Abstractions.Cqrs.Command;
using GFramework.Core.Rule;
-using Mediator;
namespace GFramework.Core.Cqrs.Command;
///
-/// 抽象流式命令处理器基类
-/// 继承自ContextAwareBase并实现IStreamCommandHandler接口,为具体的流式命令处理器提供基础功能
-/// 支持流式处理命令并产生异步可枚举的响应序列
+/// 抽象流式命令处理器基类。
+/// 继承自 并实现 ,
+/// 为具体的流式命令处理器提供基础功能。
///
-/// 流式命令类型,必须实现IStreamCommand接口
-/// 流式命令响应元素类型
+/// 流式命令类型,必须实现 。
+/// 流式命令响应元素类型。
+///
+/// 框架会在每次调用 CreateStream 进入实际处理逻辑前,为当前处理器实例注入架构上下文,
+/// 因此派生类只能在 执行期间及其返回的异步枚举序列内假定 Context 可用。
+/// 默认注册器会将流式命令处理器注册为瞬态服务,以避免同一个上下文感知实例在多个流或并发请求之间复用。
+/// 派生类不应缓存处理器实例,也不应把依赖当前上下文的可变状态泄漏到流外部。
+/// 传入 的取消令牌同时约束流的创建与后续枚举,
+/// 派生类应在启动阶段和每次生成响应前尊重取消请求,避免在调用方停止枚举后继续执行后台工作。
+///
public abstract class AbstractStreamCommandHandler : ContextAwareBase,
- IStreamCommandHandler
+ IStreamRequestHandler
where TCommand : IStreamCommand
{
///
- /// 处理流式命令并返回异步可枚举的响应序列
- /// 由具体的流式命令处理器子类实现流式处理逻辑
+ /// 处理流式命令并返回异步可枚举的响应序列。
+ /// 由具体的流式命令处理器子类实现流式处理逻辑。
///
- /// 要处理的流式命令对象
- /// 取消令牌,用于取消流式处理操作
- /// 异步可枚举的响应序列,每个元素类型为TResponse
+ /// 要处理的流式命令对象。
+ /// 取消令牌,用于取消流式处理操作。
+ /// 异步可枚举的响应序列,每个元素类型为 。
public abstract IAsyncEnumerable Handle(TCommand command, CancellationToken cancellationToken);
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Cqrs/Command/CommandBase.cs b/GFramework.Core/Cqrs/Command/CommandBase.cs
index ba0e56ad..78fa134e 100644
--- a/GFramework.Core/Cqrs/Command/CommandBase.cs
+++ b/GFramework.Core/Cqrs/Command/CommandBase.cs
@@ -12,7 +12,6 @@
// limitations under the License.
using GFramework.Core.Abstractions.Cqrs.Command;
-using Mediator;
namespace GFramework.Core.Cqrs.Command;
@@ -29,4 +28,4 @@ public abstract class CommandBase(TInput input) : ICommand
public TInput Input => input;
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs b/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
new file mode 100644
index 00000000..69f6794d
--- /dev/null
+++ b/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
@@ -0,0 +1,263 @@
+using System.Collections.Concurrent;
+using System.Reflection;
+using GFramework.Core.Abstractions.Architectures;
+using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Core.Abstractions.Ioc;
+using GFramework.Core.Abstractions.Logging;
+using GFramework.Core.Abstractions.Rule;
+
+namespace GFramework.Core.Cqrs.Internal;
+
+///
+/// GFramework 自有 CQRS 运行时分发器。
+/// 该类型负责解析请求/通知处理器,并在调用前为上下文感知对象注入当前架构上下文。
+///
+internal sealed class CqrsDispatcher(
+ IIocContainer container,
+ IArchitectureContext context,
+ ILogger logger)
+{
+ private delegate ValueTask