From 048f96c6cdd72f2038d44b3f0b9c33f9f60a2048 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 14:41:53 +0800
Subject: [PATCH 01/16] =?UTF-8?q?feat(core):=20=E6=B7=BB=E5=8A=A0=E6=9E=B6?=
=?UTF-8?q?=E6=9E=84=E4=B8=8A=E4=B8=8B=E6=96=87=E5=92=8CCQRS=E8=BF=90?=
=?UTF-8?q?=E8=A1=8C=E6=97=B6=E5=AE=9E=E7=8E=B0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 实现ArchitectureContext提供系统、模型、工具等组件访问管理
- 添加CqrsDispatcher作为GFramework自有CQRS运行时分发器
- 集成Microsoft.Extensions.DependencyInjection作为IoC容器适配器
- 实现完整的命令、查询、事件处理机制
- 支持上下文感知处理器注入架构上下文
- 提供管道行为链处理机制
- 实现流式请求处理功能
- 添加服务实例缓存和优先级排序支持
---
.../Cqrs/ICqrsRuntime.cs | 51 ++++++++
.../GFramework.Core.Abstractions.csproj | 3 +
GFramework.Core.Tests/CqrsTestRuntime.cs | 62 +++++++++
.../Ioc/MicrosoftDiContainerTests.cs | 9 +-
.../Architectures/ArchitectureContext.cs | 31 ++---
.../Cqrs/Internal/CqrsDispatcher.cs | 122 ++++++++++--------
.../Internal/DefaultCqrsHandlerRegistrar.cs | 26 ++++
GFramework.Core/Ioc/MicrosoftDiContainer.cs | 25 +++-
.../Services/Modules/CqrsRuntimeModule.cs | 61 +++++++++
.../Services/ServiceModuleManager.cs | 5 +-
.../Cqrs/Command/ICommand.cs | 0
.../Cqrs/Command/ICommandInput.cs | 2 +-
.../Cqrs/ICqrsHandlerRegistrar.cs | 17 +++
.../Cqrs/IInput.cs | 2 +-
.../Cqrs/INotification.cs | 0
.../Cqrs/INotificationHandler.cs | 0
.../Cqrs/IPipelineBehavior.cs | 0
.../Cqrs/IRequest.cs | 0
.../Cqrs/IRequestHandler.cs | 0
.../Cqrs/IStreamRequest.cs | 0
.../Cqrs/IStreamRequestHandler.cs | 0
.../Cqrs/MessageHandlerDelegate.cs | 0
.../Cqrs/Notification/INotificationInput.cs | 2 +-
.../Cqrs/Query/IQuery.cs | 0
.../Cqrs/Query/IQueryInput.cs | 2 +-
.../Cqrs/Request/IRequestInput.cs | 2 +-
.../Cqrs/Unit.cs | 0
.../Directory.Build.props | 18 +++
.../GFramework.Cqrs.Abstractions.csproj | 11 ++
GFramework.Cqrs.Abstractions/GlobalUsings.cs | 3 +
GFramework.Cqrs/GFramework.Cqrs.csproj | 16 +++
GFramework.sln | 28 ++++
32 files changed, 416 insertions(+), 82 deletions(-)
create mode 100644 GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs
create mode 100644 GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
create mode 100644 GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/Command/ICommand.cs (100%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/Command/ICommandInput.cs (84%)
create mode 100644 GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/IInput.cs (96%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/INotification.cs (100%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/INotificationHandler.cs (100%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/IPipelineBehavior.cs (100%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/IRequest.cs (100%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/IRequestHandler.cs (100%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/IStreamRequest.cs (100%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/IStreamRequestHandler.cs (100%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/MessageHandlerDelegate.cs (100%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/Notification/INotificationInput.cs (94%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/Query/IQuery.cs (100%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/Query/IQueryInput.cs (79%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/Request/IRequestInput.cs (95%)
rename {GFramework.Core.Abstractions => GFramework.Cqrs.Abstractions}/Cqrs/Unit.cs (100%)
create mode 100644 GFramework.Cqrs.Abstractions/Directory.Build.props
create mode 100644 GFramework.Cqrs.Abstractions/GFramework.Cqrs.Abstractions.csproj
create mode 100644 GFramework.Cqrs.Abstractions/GlobalUsings.cs
create mode 100644 GFramework.Cqrs/GFramework.Cqrs.csproj
diff --git a/GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs b/GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs
new file mode 100644
index 00000000..e26310d7
--- /dev/null
+++ b/GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs
@@ -0,0 +1,51 @@
+using GFramework.Core.Abstractions.Architectures;
+
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 定义架构上下文使用的 CQRS runtime seam。
+/// 该抽象把请求分发、通知发布与流式处理从具体实现中解耦,
+/// 使 不再直接依赖某个固定的 runtime 类型。
+///
+public interface ICqrsRuntime
+{
+ ///
+ /// 发送请求并返回响应。
+ ///
+ /// 响应类型。
+ /// 当前架构上下文,用于上下文感知处理器注入与嵌套请求访问。
+ /// 要分发的请求。
+ /// 取消令牌。
+ /// 请求响应。
+ ValueTask SendAsync(
+ IArchitectureContext context,
+ IRequest request,
+ CancellationToken cancellationToken = default);
+
+ ///
+ /// 发布通知到所有已注册处理器。
+ ///
+ /// 通知类型。
+ /// 当前架构上下文,用于上下文感知处理器注入。
+ /// 要发布的通知。
+ /// 取消令牌。
+ /// 表示通知分发完成的值任务。
+ ValueTask PublishAsync(
+ IArchitectureContext context,
+ TNotification notification,
+ CancellationToken cancellationToken = default)
+ where TNotification : INotification;
+
+ ///
+ /// 创建流式请求的异步响应序列。
+ ///
+ /// 流元素类型。
+ /// 当前架构上下文,用于上下文感知处理器注入。
+ /// 流式请求。
+ /// 取消令牌。
+ /// 按需生成的异步响应序列。
+ IAsyncEnumerable CreateStream(
+ IArchitectureContext context,
+ IStreamRequest request,
+ CancellationToken cancellationToken = default);
+}
diff --git a/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj b/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
index 84d53f63..7a70b332 100644
--- a/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
+++ b/GFramework.Core.Abstractions/GFramework.Core.Abstractions.csproj
@@ -17,6 +17,9 @@
+
+
+
all
diff --git a/GFramework.Core.Tests/CqrsTestRuntime.cs b/GFramework.Core.Tests/CqrsTestRuntime.cs
index e9664925..f2f64f9b 100644
--- a/GFramework.Core.Tests/CqrsTestRuntime.cs
+++ b/GFramework.Core.Tests/CqrsTestRuntime.cs
@@ -1,4 +1,5 @@
using System.Reflection;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
@@ -38,6 +39,65 @@ internal static class CqrsTestRuntime
?? throw new InvalidOperationException(
"Failed to locate CqrsHandlerRegistrar.RegisterHandlers.");
+ private static readonly Type CqrsDispatcherType = typeof(ArchitectureContext).Assembly
+ .GetType(
+ "GFramework.Core.Cqrs.Internal.CqrsDispatcher",
+ throwOnError: true)!
+ ?? throw new InvalidOperationException(
+ "Failed to locate CqrsDispatcher type.");
+
+ private static readonly ConstructorInfo CqrsDispatcherConstructor = CqrsDispatcherType.GetConstructor(
+ BindingFlags.Instance |
+ BindingFlags.Public |
+ BindingFlags.NonPublic,
+ binder: null,
+ [
+ typeof(IIocContainer),
+ typeof(ILogger)
+ ],
+ modifiers: null)
+ ?? throw new InvalidOperationException(
+ "Failed to locate CqrsDispatcher constructor.");
+
+ private static readonly Type DefaultCqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
+ .GetType(
+ "GFramework.Core.Cqrs.Internal.DefaultCqrsHandlerRegistrar",
+ throwOnError: true)!
+ ?? throw new InvalidOperationException(
+ "Failed to locate DefaultCqrsHandlerRegistrar type.");
+
+ private static readonly ConstructorInfo DefaultCqrsHandlerRegistrarConstructor =
+ DefaultCqrsHandlerRegistrarType.GetConstructor(
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
+ binder: null,
+ [
+ typeof(IIocContainer),
+ typeof(ILogger)
+ ],
+ modifiers: null)
+ ?? throw new InvalidOperationException(
+ "Failed to locate DefaultCqrsHandlerRegistrar constructor.");
+
+ ///
+ /// 为裸测试容器补齐默认 CQRS runtime seam。
+ /// 这使仅使用 的测试环境也能观察与生产路径一致的 runtime 行为,
+ /// 而无需完整启动服务模块管理器。
+ ///
+ /// 目标测试容器。
+ internal static void RegisterInfrastructure(MicrosoftDiContainer container)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+
+ var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
+ var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
+ var runtime = (ICqrsRuntime)CqrsDispatcherConstructor.Invoke([container, runtimeLogger]);
+ var registrar =
+ (ICqrsHandlerRegistrar)DefaultCqrsHandlerRegistrarConstructor.Invoke([container, registrarLogger]);
+
+ container.Register(runtime);
+ container.Register(registrar);
+ }
+
///
/// 通过与生产代码一致的注册入口扫描并注册指定程序集中的 CQRS 处理器。
///
@@ -48,6 +108,8 @@ internal static class CqrsTestRuntime
ArgumentNullException.ThrowIfNull(container);
ArgumentNullException.ThrowIfNull(assemblies);
+ RegisterInfrastructure(container);
+
var logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
RegisterHandlersMethod.Invoke(
null,
diff --git a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
index b4e5af67..7126a3ad 100644
--- a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
+++ b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
@@ -1,6 +1,5 @@
using System.Reflection;
using GFramework.Core.Abstractions.Bases;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Tests.Cqrs;
@@ -14,6 +13,8 @@ namespace GFramework.Core.Tests.Ioc;
[TestFixture]
public class MicrosoftDiContainerTests
{
+ private MicrosoftDiContainer _container = null!;
+
///
/// 在每个测试方法执行前进行设置
///
@@ -29,9 +30,9 @@ public class MicrosoftDiContainerTests
BindingFlags.NonPublic | BindingFlags.Instance);
loggerField?.SetValue(_container,
LoggerFactoryResolver.Provider.CreateLogger(nameof(MicrosoftDiContainer)));
- }
- private MicrosoftDiContainer _container = null!;
+ CqrsTestRuntime.RegisterInfrastructure(_container);
+ }
///
/// 测试注册单例实例的功能
@@ -328,6 +329,8 @@ public class MicrosoftDiContainerTests
descriptor.ServiceType == typeof(INotificationHandler)),
Is.False);
+ // Clear 会移除测试手工补齐的 CQRS seam,需要先恢复基础设施再验证程序集去重状态是否已重置。
+ CqrsTestRuntime.RegisterInfrastructure(_container);
_container.RegisterCqrsHandlersFromAssembly(assembly);
Assert.That(
diff --git a/GFramework.Core/Architectures/ArchitectureContext.cs b/GFramework.Core/Architectures/ArchitectureContext.cs
index 77c04fcf..63bb34e8 100644
--- a/GFramework.Core/Architectures/ArchitectureContext.cs
+++ b/GFramework.Core/Architectures/ArchitectureContext.cs
@@ -2,18 +2,13 @@ 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 GFramework.Core.Cqrs.Internal;
-using GFramework.Core.Logging;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Architectures;
@@ -25,15 +20,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;
+ private ICqrsRuntime? _cqrsRuntime;
#region CQRS Integration
///
- /// 获取 CQRS 运行时分发器(延迟初始化)。
+ /// 获取 CQRS runtime seam(延迟初始化)。
///
- private CqrsDispatcher CqrsDispatcher => _cqrsDispatcher ??= new CqrsDispatcher(_container, this, _logger);
+ private ICqrsRuntime CqrsRuntime => _cqrsRuntime ??=
+ _container.Get() ?? throw new InvalidOperationException("ICqrsRuntime not registered");
///
/// 获取指定类型的服务实例,如果缓存中存在则直接返回,否则从容器中获取并缓存
@@ -73,7 +68,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
- return await CqrsDispatcher.SendAsync(request, cancellationToken);
+ return await CqrsRuntime.SendAsync(this, request, cancellationToken);
}
///
@@ -100,7 +95,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
where TNotification : INotification
{
ArgumentNullException.ThrowIfNull(notification);
- await CqrsDispatcher.PublishAsync(notification, cancellationToken);
+ await CqrsRuntime.PublishAsync(this, notification, cancellationToken);
}
///
@@ -115,7 +110,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(request);
- return CqrsDispatcher.CreateStream(request, cancellationToken);
+ return CqrsRuntime.CreateStream(this, request, cancellationToken);
}
///
@@ -151,7 +146,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 查询结果类型
/// 要发送的查询
/// 查询结果
- public TResult SendQuery(Abstractions.Query.IQuery query)
+ public TResult SendQuery(IQuery query)
{
if (query == null) throw new ArgumentNullException(nameof(query));
var queryBus = GetOrCache();
@@ -165,7 +160,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 查询响应类型
/// 要发送的查询对象
/// 查询结果
- public TResponse SendQuery(GFramework.Core.Abstractions.Cqrs.Query.IQuery query)
+ public TResponse SendQuery(Abstractions.Cqrs.Query.IQuery query)
{
return SendQueryAsync(query).AsTask().GetAwaiter().GetResult();
}
@@ -191,7 +186,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 要发送的查询对象
/// 取消令牌,用于取消操作
/// 包含查询结果的ValueTask
- public async ValueTask SendQueryAsync(GFramework.Core.Abstractions.Cqrs.Query.IQuery query,
+ public async ValueTask SendQueryAsync(Abstractions.Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(query);
@@ -327,7 +322,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 要发送的命令对象
/// 取消令牌,用于取消操作
/// 包含命令执行结果的ValueTask
- public async ValueTask SendCommandAsync(GFramework.Core.Abstractions.Cqrs.Command.ICommand command,
+ public async ValueTask SendCommandAsync(Abstractions.Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(command);
@@ -366,7 +361,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 命令响应类型
/// 要发送的命令对象
/// 命令执行结果
- public TResponse SendCommand(GFramework.Core.Abstractions.Cqrs.Command.ICommand command)
+ public TResponse SendCommand(Abstractions.Cqrs.Command.ICommand command)
{
return SendCommandAsync(command).AsTask().GetAwaiter().GetResult();
}
@@ -388,7 +383,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 命令执行结果类型
/// 要发送的命令
/// 命令执行结果
- public TResult SendCommand(Abstractions.Command.ICommand command)
+ public TResult SendCommand(ICommand command)
{
ArgumentNullException.ThrowIfNull(command);
var commandBus = GetOrCache();
diff --git a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs b/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
index 69f6794d..f1950cf7 100644
--- a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
+++ b/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
@@ -14,34 +14,70 @@ namespace GFramework.Core.Cqrs.Internal;
///
internal sealed class CqrsDispatcher(
IIocContainer container,
- IArchitectureContext context,
- ILogger logger)
+ ILogger logger) : ICqrsRuntime
{
- private delegate ValueTask
/// 处理器实例。
- private void PrepareHandler(object handler)
+ /// 当前架构上下文。
+ private static void PrepareHandler(object handler, IArchitectureContext context)
{
if (handler is IContextAware contextAware)
contextAware.SetContext(context);
@@ -260,4 +266,18 @@ internal sealed class CqrsDispatcher(
var typedRequest = (TRequest)request;
return typedHandler.Handle(typedRequest, cancellationToken);
}
+
+ private delegate ValueTask RequestInvoker(object handler, object request,
+ CancellationToken cancellationToken);
+
+ private delegate ValueTask RequestPipelineInvoker(
+ object handler,
+ IReadOnlyList behaviors,
+ object request,
+ CancellationToken cancellationToken);
+
+ private delegate ValueTask NotificationInvoker(object handler, object notification,
+ CancellationToken cancellationToken);
+
+ private delegate object StreamInvoker(object handler, object request, CancellationToken cancellationToken);
}
diff --git a/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs b/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
new file mode 100644
index 00000000..5d59f8a6
--- /dev/null
+++ b/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
@@ -0,0 +1,26 @@
+using System.Reflection;
+using GFramework.Core.Abstractions.Ioc;
+using GFramework.Core.Abstractions.Logging;
+
+namespace GFramework.Core.Cqrs.Internal;
+
+///
+/// 默认的 CQRS 处理器注册器实现。
+/// 该适配器把容器公开的 handler 接入入口转发到现有的注册流水线,
+/// 使容器主路径只依赖 抽象。
+///
+internal sealed class DefaultCqrsHandlerRegistrar(IIocContainer container, ILogger logger) : ICqrsHandlerRegistrar
+{
+ private readonly IIocContainer _container = container ?? throw new ArgumentNullException(nameof(container));
+ private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger));
+
+ ///
+ /// 按当前 runtime 约定扫描并注册处理器程序集。
+ ///
+ /// 要接入的程序集集合。
+ public void RegisterHandlers(IEnumerable assemblies)
+ {
+ ArgumentNullException.ThrowIfNull(assemblies);
+ CqrsHandlerRegistrar.RegisterHandlers(_container, assemblies, _logger);
+ }
+}
diff --git a/GFramework.Core/Ioc/MicrosoftDiContainer.cs b/GFramework.Core/Ioc/MicrosoftDiContainer.cs
index 712a41c1..c094647d 100644
--- a/GFramework.Core/Ioc/MicrosoftDiContainer.cs
+++ b/GFramework.Core/Ioc/MicrosoftDiContainer.cs
@@ -1,11 +1,9 @@
using System.ComponentModel;
using System.Reflection;
using GFramework.Core.Abstractions.Bases;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Systems;
-using GFramework.Core.Cqrs.Internal;
using GFramework.Core.Logging;
using GFramework.Core.Rule;
@@ -424,7 +422,7 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
continue;
}
- CqrsHandlerRegistrar.RegisterHandlers(this, [assembly], _logger);
+ ResolveCqrsHandlerRegistrar().RegisterHandlers([assembly]);
_registeredCqrsHandlerAssemblyKeys.Add(assemblyKey);
}
}
@@ -456,6 +454,27 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
#region Get
+ ///
+ /// 获取当前容器中已注册的 CQRS 处理器注册器。
+ /// 该方法仅供容器内部在注册阶段使用,因此直接读取服务描述符中的实例绑定,
+ /// 避免在容器未冻结前依赖完整的服务提供者构建流程。
+ ///
+ /// 已注册的 CQRS 处理器注册器实例。
+ /// 未找到可用的 CQRS 处理器注册器实例时抛出。
+ private ICqrsHandlerRegistrar ResolveCqrsHandlerRegistrar()
+ {
+ var descriptor = GetServicesUnsafe.LastOrDefault(static service =>
+ service.ServiceType == typeof(ICqrsHandlerRegistrar));
+
+ if (descriptor?.ImplementationInstance is ICqrsHandlerRegistrar registrar)
+ return registrar;
+
+ const string errorMessage =
+ "ICqrsHandlerRegistrar not registered. Ensure the CQRS runtime module has been installed before registering handlers.";
+ _logger.Error(errorMessage);
+ throw new InvalidOperationException(errorMessage);
+ }
+
///
/// 获取指定泛型类型的服务实例
/// 返回第一个匹配的注册实例,如果不存在则返回null
diff --git a/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs b/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
new file mode 100644
index 00000000..915e07e2
--- /dev/null
+++ b/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
@@ -0,0 +1,61 @@
+using GFramework.Core.Abstractions.Architectures;
+using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Core.Abstractions.Ioc;
+using GFramework.Core.Cqrs.Internal;
+using GFramework.Core.Logging;
+
+namespace GFramework.Core.Services.Modules;
+
+///
+/// CQRS runtime 模块,用于把默认请求分发器与处理器注册器接入架构容器。
+/// 该模块在架构初始化早期完成注册,保证用户初始化阶段即可使用 CQRS 入口与 handler 自动接入能力。
+///
+public sealed class CqrsRuntimeModule : IServiceModule
+{
+ ///
+ /// 获取模块名称。
+ ///
+ public string ModuleName => nameof(CqrsRuntimeModule);
+
+ ///
+ /// 获取模块优先级。
+ /// CQRS runtime 需要先于架构默认 handler 扫描路径可用,因此放在基础总线模块之后、用户初始化之前注册。
+ ///
+ public int Priority => 15;
+
+ ///
+ /// 获取模块启用状态,默认启用。
+ ///
+ public bool IsEnabled => true;
+
+ ///
+ /// 注册默认 CQRS runtime seam 实现。
+ ///
+ /// 目标依赖注入容器。
+ public void Register(IIocContainer container)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+
+ var dispatcherLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsDispatcher));
+ var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(DefaultCqrsHandlerRegistrar));
+
+ container.Register(new CqrsDispatcher(container, dispatcherLogger));
+ container.Register(new DefaultCqrsHandlerRegistrar(container, registrarLogger));
+ }
+
+ ///
+ /// 初始化模块。
+ ///
+ public void Initialize()
+ {
+ }
+
+ ///
+ /// 异步销毁模块。
+ ///
+ /// 已完成的值任务。
+ public ValueTask DestroyAsync()
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/GFramework.Core/Services/ServiceModuleManager.cs b/GFramework.Core/Services/ServiceModuleManager.cs
index d07c128b..a3965f4d 100644
--- a/GFramework.Core/Services/ServiceModuleManager.cs
+++ b/GFramework.Core/Services/ServiceModuleManager.cs
@@ -42,7 +42,7 @@ public sealed class ServiceModuleManager : IServiceModuleManager
///
/// 注册内置服务模块,并根据优先级排序后完成服务注册。
- /// 内置模块包括事件总线、命令执行器、查询执行器等核心模块。
+ /// 内置模块包括事件总线、命令执行器、CQRS runtime、查询执行器等核心模块。
/// 同时注册通过 ArchitectureModuleRegistry 自动注册的外部模块。
///
/// IoC容器实例,用于模块服务注册。
@@ -57,6 +57,7 @@ public sealed class ServiceModuleManager : IServiceModuleManager
// 注册内置模块
RegisterModule(new EventBusModule());
RegisterModule(new CommandExecutorModule());
+ RegisterModule(new CqrsRuntimeModule());
RegisterModule(new QueryExecutorModule());
RegisterModule(new AsyncQueryExecutorModule());
@@ -148,4 +149,4 @@ public sealed class ServiceModuleManager : IServiceModuleManager
_builtInModulesRegistered = false;
_logger.Info("All service modules destroyed");
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/Command/ICommand.cs b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/Command/ICommand.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/Command/ICommandInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommandInput.cs
similarity index 84%
rename from GFramework.Core.Abstractions/Cqrs/Command/ICommandInput.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Command/ICommandInput.cs
index 5ec607e5..a00d67e6 100644
--- a/GFramework.Core.Abstractions/Cqrs/Command/ICommandInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommandInput.cs
@@ -4,4 +4,4 @@
/// 命令输入接口,定义命令模式中输入数据的契约
/// 该接口作为标记接口使用,不包含任何成员定义
///
-public interface ICommandInput : IInput;
\ No newline at end of file
+public interface ICommandInput : IInput;
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs b/GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs
new file mode 100644
index 00000000..0257b3ec
--- /dev/null
+++ b/GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs
@@ -0,0 +1,17 @@
+using System.Reflection;
+
+namespace GFramework.Core.Abstractions.Cqrs;
+
+///
+/// 定义 CQRS 处理器程序集接入的 runtime seam。
+/// 该抽象负责承接“生成注册器优先、反射扫描回退”的处理器注册流程,
+/// 让容器与架构启动链不再直接依赖固定的注册实现类型。
+///
+public interface ICqrsHandlerRegistrar
+{
+ ///
+ /// 扫描并注册指定程序集集合中的 CQRS 处理器。
+ ///
+ /// 要接入的程序集集合。
+ void RegisterHandlers(IEnumerable assemblies);
+}
diff --git a/GFramework.Core.Abstractions/Cqrs/IInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/IInput.cs
similarity index 96%
rename from GFramework.Core.Abstractions/Cqrs/IInput.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IInput.cs
index dfed5012..ddf00622 100644
--- a/GFramework.Core.Abstractions/Cqrs/IInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/IInput.cs
@@ -17,4 +17,4 @@ namespace GFramework.Core.Abstractions.Cqrs;
/// 表示输入数据的标记接口。
/// 该接口用于标识各类CQRS模式中的输入参数类型。
///
-public interface IInput;
\ No newline at end of file
+public interface IInput;
diff --git a/GFramework.Core.Abstractions/Cqrs/INotification.cs b/GFramework.Cqrs.Abstractions/Cqrs/INotification.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/INotification.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/INotification.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/INotificationHandler.cs b/GFramework.Cqrs.Abstractions/Cqrs/INotificationHandler.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/INotificationHandler.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/INotificationHandler.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/IPipelineBehavior.cs b/GFramework.Cqrs.Abstractions/Cqrs/IPipelineBehavior.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/IPipelineBehavior.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IPipelineBehavior.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/IRequest.cs b/GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/IRequest.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/IRequestHandler.cs b/GFramework.Cqrs.Abstractions/Cqrs/IRequestHandler.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/IRequestHandler.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IRequestHandler.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/IStreamRequest.cs b/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/IStreamRequest.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/IStreamRequestHandler.cs b/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequestHandler.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/IStreamRequestHandler.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/IStreamRequestHandler.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/MessageHandlerDelegate.cs b/GFramework.Cqrs.Abstractions/Cqrs/MessageHandlerDelegate.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/MessageHandlerDelegate.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/MessageHandlerDelegate.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/Notification/INotificationInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Notification/INotificationInput.cs
similarity index 94%
rename from GFramework.Core.Abstractions/Cqrs/Notification/INotificationInput.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Notification/INotificationInput.cs
index 8b791839..236b30ff 100644
--- a/GFramework.Core.Abstractions/Cqrs/Notification/INotificationInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Notification/INotificationInput.cs
@@ -17,4 +17,4 @@ namespace GFramework.Core.Abstractions.Cqrs.Notification;
/// 表示通知输入数据的标记接口。
/// 该接口继承自 IInput,用于标识CQRS模式中通知类型的输入参数。
///
-public interface INotificationInput : IInput;
\ No newline at end of file
+public interface INotificationInput : IInput;
diff --git a/GFramework.Core.Abstractions/Cqrs/Query/IQuery.cs b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/Query/IQuery.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs
diff --git a/GFramework.Core.Abstractions/Cqrs/Query/IQueryInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQueryInput.cs
similarity index 79%
rename from GFramework.Core.Abstractions/Cqrs/Query/IQueryInput.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Query/IQueryInput.cs
index c505c4ff..7e0a5b4f 100644
--- a/GFramework.Core.Abstractions/Cqrs/Query/IQueryInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQueryInput.cs
@@ -3,4 +3,4 @@
///
/// 查询输入接口,定义了查询操作的输入规范
///
-public interface IQueryInput : IInput;
\ No newline at end of file
+public interface IQueryInput : IInput;
diff --git a/GFramework.Core.Abstractions/Cqrs/Request/IRequestInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Request/IRequestInput.cs
similarity index 95%
rename from GFramework.Core.Abstractions/Cqrs/Request/IRequestInput.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Request/IRequestInput.cs
index 0a0b1591..7b7ff83c 100644
--- a/GFramework.Core.Abstractions/Cqrs/Request/IRequestInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Request/IRequestInput.cs
@@ -17,4 +17,4 @@ namespace GFramework.Core.Abstractions.Cqrs.Request;
/// 表示请求输入数据的标记接口。
/// 该接口继承自 IInput,用于标识CQRS模式中请求类型的输入参数。
///
-public interface IRequestInput : IInput;
\ No newline at end of file
+public interface IRequestInput : IInput;
diff --git a/GFramework.Core.Abstractions/Cqrs/Unit.cs b/GFramework.Cqrs.Abstractions/Cqrs/Unit.cs
similarity index 100%
rename from GFramework.Core.Abstractions/Cqrs/Unit.cs
rename to GFramework.Cqrs.Abstractions/Cqrs/Unit.cs
diff --git a/GFramework.Cqrs.Abstractions/Directory.Build.props b/GFramework.Cqrs.Abstractions/Directory.Build.props
new file mode 100644
index 00000000..8febdc42
--- /dev/null
+++ b/GFramework.Cqrs.Abstractions/Directory.Build.props
@@ -0,0 +1,18 @@
+
+
+ netstandard2.1
+ true
+ true
+ preview
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+ all
+ runtime; build; native; contentfiles; analyzers
+
+
+
diff --git a/GFramework.Cqrs.Abstractions/GFramework.Cqrs.Abstractions.csproj b/GFramework.Cqrs.Abstractions/GFramework.Cqrs.Abstractions.csproj
new file mode 100644
index 00000000..8e07fd2c
--- /dev/null
+++ b/GFramework.Cqrs.Abstractions/GFramework.Cqrs.Abstractions.csproj
@@ -0,0 +1,11 @@
+
+
+
+ GeWuYou.$(AssemblyName)
+ true
+ T:System.Diagnostics.CodeAnalysis.NotNullWhenAttribute
+ enable
+ true
+
+
+
diff --git a/GFramework.Cqrs.Abstractions/GlobalUsings.cs b/GFramework.Cqrs.Abstractions/GlobalUsings.cs
new file mode 100644
index 00000000..5cd04a4e
--- /dev/null
+++ b/GFramework.Cqrs.Abstractions/GlobalUsings.cs
@@ -0,0 +1,3 @@
+global using System.Collections.Generic;
+global using System.Threading;
+global using System.Threading.Tasks;
diff --git a/GFramework.Cqrs/GFramework.Cqrs.csproj b/GFramework.Cqrs/GFramework.Cqrs.csproj
new file mode 100644
index 00000000..9f002283
--- /dev/null
+++ b/GFramework.Cqrs/GFramework.Cqrs.csproj
@@ -0,0 +1,16 @@
+
+
+
+ GeWuYou.$(AssemblyName)
+ net8.0;net9.0;net10.0
+ disable
+ enable
+ true
+ true
+
+
+
+
+
+
+
diff --git a/GFramework.sln b/GFramework.sln
index 6d4eb45f..67d53c69 100644
--- a/GFramework.sln
+++ b/GFramework.sln
@@ -38,6 +38,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.SourceGene
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Godot.Tests", "GFramework.Godot.Tests\GFramework.Godot.Tests.csproj", "{576119E2-13D0-4ACF-A012-D01C320E8BF3}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs.Abstractions", "GFramework.Cqrs.Abstractions\GFramework.Cqrs.Abstractions.csproj", "{69C06523-98AA-49DE-95D4-4BF203716DD2}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs", "GFramework.Cqrs\GFramework.Cqrs.csproj", "{E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -276,6 +280,30 @@ Global
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|x64.Build.0 = Release|Any CPU
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|x86.ActiveCfg = Release|Any CPU
{576119E2-13D0-4ACF-A012-D01C320E8BF3}.Release|x86.Build.0 = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|x64.Build.0 = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Debug|x86.Build.0 = Debug|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|x64.ActiveCfg = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|x64.Build.0 = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|x86.ActiveCfg = Release|Any CPU
+ {69C06523-98AA-49DE-95D4-4BF203716DD2}.Release|x86.Build.0 = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|x64.Build.0 = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Debug|x86.Build.0 = Debug|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x64.ActiveCfg = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x64.Build.0 = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x86.ActiveCfg = Release|Any CPU
+ {E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
From 34e140e91997b78614e4124abc28053c99e8bcb0 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:13:43 +0800
Subject: [PATCH 02/16] =?UTF-8?q?feat(ioc):=20=E6=B7=BB=E5=8A=A0=20Microso?=
=?UTF-8?q?ft=20DI=20=E5=AE=B9=E5=99=A8=E9=80=82=E9=85=8D=E5=99=A8?=
=?UTF-8?q?=E5=92=8C=20CI/CD=20=E5=B7=A5=E4=BD=9C=E6=B5=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 实现 MicrosoftDiContainer 类,提供 Microsoft.Extensions.DependencyInjection 的适配器
- 添加 DefaultCqrsHandlerRegistrar 默认 CQRS 处理器注册器实现
- 配置 GitHub Actions CI/CD 工作流,包含代码质量检查和构建测试任务
- 设置 .NET 8/9/10 多版本支持和缓存策略
- 添加单元测试覆盖 IoC 容器的各项功能,包括注册、解析和生命周期管理
- 实现线程安全的读写锁机制保护容器操作
- 支持 CQRS 处理器和管道行为的注册管理
---
.github/workflows/ci.yml | 6 +
.../Cqrs/ContainerRegistrationFixtures.cs | 36 ++++++
.../Ioc/MicrosoftDiContainerTests.cs | 2 +-
.../Internal/DefaultCqrsHandlerRegistrar.cs | 1 +
GFramework.Core/GFramework.Core.csproj | 1 +
GFramework.Core/Ioc/MicrosoftDiContainer.cs | 1 +
.../Coroutine/CqrsCoroutineExtensionsTests.cs | 0
.../Cqrs/CqrsHandlerRegistrarTests.cs | 1 -
GFramework.Cqrs.Tests/CqrsTestRuntime.cs | 117 ++++++++++++++++++
.../GFramework.Cqrs.Tests.csproj | 26 ++++
GFramework.Cqrs.Tests/GlobalUsings.cs | 26 ++++
GFramework.Cqrs.Tests/Logging/TestLogger.cs | 56 +++++++++
.../Mediator/MediatorAdvancedFeaturesTests.cs | 2 -
.../MediatorArchitectureIntegrationTests.cs | 2 -
.../Mediator/MediatorComprehensiveTests.cs | 3 -
GFramework.csproj | 9 ++
GFramework.sln | 14 +++
17 files changed, 294 insertions(+), 9 deletions(-)
create mode 100644 GFramework.Core.Tests/Cqrs/ContainerRegistrationFixtures.cs
rename {GFramework.Core.Tests => GFramework.Cqrs.Tests}/Coroutine/CqrsCoroutineExtensionsTests.cs (100%)
rename {GFramework.Core.Tests => GFramework.Cqrs.Tests}/Cqrs/CqrsHandlerRegistrarTests.cs (99%)
create mode 100644 GFramework.Cqrs.Tests/CqrsTestRuntime.cs
create mode 100644 GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
create mode 100644 GFramework.Cqrs.Tests/GlobalUsings.cs
create mode 100644 GFramework.Cqrs.Tests/Logging/TestLogger.cs
rename {GFramework.Core.Tests => GFramework.Cqrs.Tests}/Mediator/MediatorAdvancedFeaturesTests.cs (99%)
rename {GFramework.Core.Tests => GFramework.Cqrs.Tests}/Mediator/MediatorArchitectureIntegrationTests.cs (99%)
rename {GFramework.Core.Tests => GFramework.Cqrs.Tests}/Mediator/MediatorComprehensiveTests.cs (99%)
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index c9bfcf89..defe6c71 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -166,6 +166,12 @@ jobs:
--logger "trx;LogFileName=sg-$RANDOM.trx" \
--results-directory TestResults &
+ dotnet test GFramework.Cqrs.Tests \
+ -c Release \
+ --no-build \
+ --logger "trx;LogFileName=cqrs-$RANDOM.trx" \
+ --results-directory TestResults &
+
dotnet test GFramework.Ecs.Arch.Tests \
-c Release \
--no-build \
diff --git a/GFramework.Core.Tests/Cqrs/ContainerRegistrationFixtures.cs b/GFramework.Core.Tests/Cqrs/ContainerRegistrationFixtures.cs
new file mode 100644
index 00000000..f01af4de
--- /dev/null
+++ b/GFramework.Core.Tests/Cqrs/ContainerRegistrationFixtures.cs
@@ -0,0 +1,36 @@
+// Copyright (c) 2026 GeWuYou
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace GFramework.Core.Tests.Cqrs;
+
+///
+/// 为容器层测试提供可扫描的最小通知夹具。
+///
+internal sealed record DeterministicOrderNotification : INotification;
+
+///
+/// 供容器注册测试验证程序集扫描结果的通知处理器。
+///
+internal sealed class DeterministicOrderNotificationHandler : INotificationHandler
+{
+ ///
+ /// 无副作用地消费通知。
+ ///
+ /// 通知实例。
+ /// 取消令牌。
+ /// 已完成任务。
+ public ValueTask Handle(DeterministicOrderNotification notification, CancellationToken cancellationToken)
+ {
+ return ValueTask.CompletedTask;
+ }
+}
diff --git a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
index 7126a3ad..6ae0747c 100644
--- a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
+++ b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
@@ -315,7 +315,7 @@ public class MicrosoftDiContainerTests
[Test]
public void Clear_Should_Reset_Cqrs_Assembly_Deduplication_State()
{
- var assembly = typeof(CqrsHandlerRegistrarTests).Assembly;
+ var assembly = typeof(DeterministicOrderNotification).Assembly;
_container.RegisterCqrsHandlersFromAssembly(assembly);
Assert.That(
diff --git a/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs b/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
index 5d59f8a6..dca76290 100644
--- a/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
+++ b/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
@@ -1,4 +1,5 @@
using System.Reflection;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
diff --git a/GFramework.Core/GFramework.Core.csproj b/GFramework.Core/GFramework.Core.csproj
index c450b44c..f3e41eab 100644
--- a/GFramework.Core/GFramework.Core.csproj
+++ b/GFramework.Core/GFramework.Core.csproj
@@ -9,6 +9,7 @@
true
+
diff --git a/GFramework.Core/Ioc/MicrosoftDiContainer.cs b/GFramework.Core/Ioc/MicrosoftDiContainer.cs
index c094647d..49ce44f2 100644
--- a/GFramework.Core/Ioc/MicrosoftDiContainer.cs
+++ b/GFramework.Core/Ioc/MicrosoftDiContainer.cs
@@ -1,6 +1,7 @@
using System.ComponentModel;
using System.Reflection;
using GFramework.Core.Abstractions.Bases;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Systems;
diff --git a/GFramework.Core.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs b/GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs
similarity index 100%
rename from GFramework.Core.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs
rename to GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs
diff --git a/GFramework.Core.Tests/Cqrs/CqrsHandlerRegistrarTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
similarity index 99%
rename from GFramework.Core.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
rename to GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
index 7748227c..05a8b0c7 100644
--- a/GFramework.Core.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
+++ b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
@@ -1,4 +1,3 @@
-using System.Reflection;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
diff --git a/GFramework.Cqrs.Tests/CqrsTestRuntime.cs b/GFramework.Cqrs.Tests/CqrsTestRuntime.cs
new file mode 100644
index 00000000..f9bb143b
--- /dev/null
+++ b/GFramework.Cqrs.Tests/CqrsTestRuntime.cs
@@ -0,0 +1,117 @@
+using GFramework.Core.Abstractions.Cqrs;
+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.");
+
+ private static readonly Type CqrsDispatcherType = typeof(ArchitectureContext).Assembly
+ .GetType(
+ "GFramework.Core.Cqrs.Internal.CqrsDispatcher",
+ throwOnError: true)!
+ ?? throw new InvalidOperationException(
+ "Failed to locate CqrsDispatcher type.");
+
+ private static readonly ConstructorInfo CqrsDispatcherConstructor = CqrsDispatcherType.GetConstructor(
+ BindingFlags.Instance |
+ BindingFlags.Public |
+ BindingFlags.NonPublic,
+ binder: null,
+ [
+ typeof(IIocContainer),
+ typeof(ILogger)
+ ],
+ modifiers: null)
+ ?? throw new InvalidOperationException(
+ "Failed to locate CqrsDispatcher constructor.");
+
+ private static readonly Type DefaultCqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
+ .GetType(
+ "GFramework.Core.Cqrs.Internal.DefaultCqrsHandlerRegistrar",
+ throwOnError: true)!
+ ?? throw new InvalidOperationException(
+ "Failed to locate DefaultCqrsHandlerRegistrar type.");
+
+ private static readonly ConstructorInfo DefaultCqrsHandlerRegistrarConstructor =
+ DefaultCqrsHandlerRegistrarType.GetConstructor(
+ BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
+ binder: null,
+ [
+ typeof(IIocContainer),
+ typeof(ILogger)
+ ],
+ modifiers: null)
+ ?? throw new InvalidOperationException(
+ "Failed to locate DefaultCqrsHandlerRegistrar constructor.");
+
+ ///
+ /// 为裸测试容器补齐默认 CQRS runtime seam。
+ /// 这使仅使用 的测试环境也能观察与生产路径一致的 runtime 行为,
+ /// 而无需完整启动服务模块管理器。
+ ///
+ /// 目标测试容器。
+ internal static void RegisterInfrastructure(MicrosoftDiContainer container)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+
+ var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
+ var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
+ var runtime = (ICqrsRuntime)CqrsDispatcherConstructor.Invoke([container, runtimeLogger]);
+ var registrar =
+ (ICqrsHandlerRegistrar)DefaultCqrsHandlerRegistrarConstructor.Invoke([container, registrarLogger]);
+
+ container.Register(runtime);
+ container.Register(registrar);
+ }
+
+ ///
+ /// 通过与生产代码一致的注册入口扫描并注册指定程序集中的 CQRS 处理器。
+ ///
+ /// 承载处理器映射的测试容器。
+ /// 要扫描的程序集集合。
+ internal static void RegisterHandlers(MicrosoftDiContainer container, params Assembly[] assemblies)
+ {
+ ArgumentNullException.ThrowIfNull(container);
+ ArgumentNullException.ThrowIfNull(assemblies);
+
+ RegisterInfrastructure(container);
+
+ 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.Cqrs.Tests/GFramework.Cqrs.Tests.csproj b/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
new file mode 100644
index 00000000..acbcc536
--- /dev/null
+++ b/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net10.0
+ $(TestTargetFrameworks)
+ disable
+ enable
+ false
+ true
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/GFramework.Cqrs.Tests/GlobalUsings.cs b/GFramework.Cqrs.Tests/GlobalUsings.cs
new file mode 100644
index 00000000..d31630ed
--- /dev/null
+++ b/GFramework.Cqrs.Tests/GlobalUsings.cs
@@ -0,0 +1,26 @@
+// Copyright (c) 2026 GeWuYou
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+global using System;
+global using System.Collections;
+global using System.Collections.Generic;
+global using System.Diagnostics;
+global using System.Linq;
+global using System.Reflection;
+global using System.Runtime.CompilerServices;
+global using System.Threading;
+global using System.Threading.Tasks;
+global using Microsoft.Extensions.DependencyInjection;
+global using Moq;
+global using NUnit.Compatibility;
+global using NUnit.Framework;
diff --git a/GFramework.Cqrs.Tests/Logging/TestLogger.cs b/GFramework.Cqrs.Tests/Logging/TestLogger.cs
new file mode 100644
index 00000000..aaf65d22
--- /dev/null
+++ b/GFramework.Cqrs.Tests/Logging/TestLogger.cs
@@ -0,0 +1,56 @@
+// Copyright (c) 2026 GeWuYou
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using GFramework.Core.Abstractions.Logging;
+using GFramework.Core.Logging;
+
+namespace GFramework.Core.Tests.Logging;
+
+///
+/// 供 CQRS 测试项目复用的最小日志记录器实现。
+///
+public sealed class TestLogger : AbstractLogger
+{
+ ///
+ /// 初始化测试日志记录器。
+ ///
+ /// 日志名称。
+ /// 最小日志级别。
+ public TestLogger(string? name = null, LogLevel minLevel = LogLevel.Info) : base(name, minLevel)
+ {
+ }
+
+ ///
+ /// 获取当前测试期间捕获到的日志条目。
+ ///
+ public List Logs { get; } = [];
+
+ ///
+ /// 将日志写入内存,供断言使用。
+ ///
+ /// 日志级别。
+ /// 日志消息。
+ /// 关联异常。
+ protected override void Write(LogLevel level, string message, Exception? exception)
+ {
+ Logs.Add(new LogEntry(level, message, exception));
+ }
+
+ ///
+ /// 表示单条测试日志记录。
+ ///
+ /// 日志级别。
+ /// 日志消息。
+ /// 关联异常。
+ public sealed record LogEntry(LogLevel Level, string Message, Exception? Exception);
+}
diff --git a/GFramework.Core.Tests/Mediator/MediatorAdvancedFeaturesTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
similarity index 99%
rename from GFramework.Core.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
rename to GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
index 2dc2503a..cadb340b 100644
--- a/GFramework.Core.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
+++ b/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
@@ -1,5 +1,3 @@
-using System.Diagnostics;
-using System.Reflection;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Architectures;
using GFramework.Core.Ioc;
diff --git a/GFramework.Core.Tests/Mediator/MediatorArchitectureIntegrationTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
similarity index 99%
rename from GFramework.Core.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
rename to GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
index e176cce5..7d73a45e 100644
--- a/GFramework.Core.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
+++ b/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
@@ -1,5 +1,3 @@
-using System.Diagnostics;
-using System.Reflection;
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Architectures;
diff --git a/GFramework.Core.Tests/Mediator/MediatorComprehensiveTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs
similarity index 99%
rename from GFramework.Core.Tests/Mediator/MediatorComprehensiveTests.cs
rename to GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs
index 27dfed5c..cdaf9af6 100644
--- a/GFramework.Core.Tests/Mediator/MediatorComprehensiveTests.cs
+++ b/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs
@@ -1,6 +1,3 @@
-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;
diff --git a/GFramework.csproj b/GFramework.csproj
index 76f9a088..679cbf64 100644
--- a/GFramework.csproj
+++ b/GFramework.csproj
@@ -63,6 +63,9 @@
+
+
+
@@ -104,6 +107,9 @@
+
+
+
@@ -131,6 +137,9 @@
+
+
+
diff --git a/GFramework.sln b/GFramework.sln
index 67d53c69..088d63d3 100644
--- a/GFramework.sln
+++ b/GFramework.sln
@@ -42,6 +42,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs.Abstraction
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs", "GFramework.Cqrs\GFramework.Cqrs.csproj", "{E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs.Tests", "GFramework.Cqrs.Tests\GFramework.Cqrs.Tests.csproj", "{29037A55-9A89-425C-AB33-D44872B2E601}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -304,6 +306,18 @@ Global
{E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x64.Build.0 = Release|Any CPU
{E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x86.ActiveCfg = Release|Any CPU
{E7034F34-0D2B-4D99-B8E2-D149EF6C88F2}.Release|x86.Build.0 = Release|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Debug|x64.Build.0 = Debug|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Debug|x86.Build.0 = Debug|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Release|Any CPU.Build.0 = Release|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Release|x64.ActiveCfg = Release|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Release|x64.Build.0 = Release|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Release|x86.ActiveCfg = Release|Any CPU
+ {29037A55-9A89-425C-AB33-D44872B2E601}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
From 49ed5d0d0686e167ce0dccf7ccc02d54c9adbf01 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:22:00 +0800
Subject: [PATCH 03/16] =?UTF-8?q?refactor(tests):=20=E6=B7=BB=E5=8A=A0CQRS?=
=?UTF-8?q?=E6=8A=BD=E8=B1=A1=E5=B1=82=E4=BE=9D=E8=B5=96=E9=A1=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 在ContainerRegistrationFixtures.cs中添加GFramework.Core.Abstractions.Cqrs命名空间引用
- 在MicrosoftDiContainerTests.cs中添加GFramework.Core.Abstractions.Cqrs命名空间引用
- 统一测试文件中的依赖注入配置
- 确保CQRS相关接口的正确引用路径
---
GFramework.Core.Tests/Cqrs/ContainerRegistrationFixtures.cs | 2 ++
GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs | 5 +++--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/GFramework.Core.Tests/Cqrs/ContainerRegistrationFixtures.cs b/GFramework.Core.Tests/Cqrs/ContainerRegistrationFixtures.cs
index f01af4de..58483c15 100644
--- a/GFramework.Core.Tests/Cqrs/ContainerRegistrationFixtures.cs
+++ b/GFramework.Core.Tests/Cqrs/ContainerRegistrationFixtures.cs
@@ -11,6 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using GFramework.Core.Abstractions.Cqrs;
+
namespace GFramework.Core.Tests.Cqrs;
///
diff --git a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
index 6ae0747c..186248ff 100644
--- a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
+++ b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
@@ -1,5 +1,6 @@
using System.Reflection;
using GFramework.Core.Abstractions.Bases;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Tests.Cqrs;
@@ -13,8 +14,6 @@ namespace GFramework.Core.Tests.Ioc;
[TestFixture]
public class MicrosoftDiContainerTests
{
- private MicrosoftDiContainer _container = null!;
-
///
/// 在每个测试方法执行前进行设置
///
@@ -34,6 +33,8 @@ public class MicrosoftDiContainerTests
CqrsTestRuntime.RegisterInfrastructure(_container);
}
+ private MicrosoftDiContainer _container = null!;
+
///
/// 测试注册单例实例的功能
///
From 1c5c5c812ad7ccfc35e4c30e36eaafa96405a6a7 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:26:38 +0800
Subject: [PATCH 04/16] =?UTF-8?q?chore(deps):=20=E6=9B=B4=E6=96=B0=20Mezia?=
=?UTF-8?q?ntou.Analyzer=20=E5=92=8C=20Meziantou.Polyfill=20=E4=BE=9D?=
=?UTF-8?q?=E8=B5=96=E5=8C=85=E7=89=88=E6=9C=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将 Meziantou.Analyzer 从 2.0.264 版本升级到 3.0.46 版本
- 将 Meziantou.Polyfill 从 1.0.71 版本升级到 1.0.109 版本
---
GFramework.Cqrs.Abstractions/Directory.Build.props | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/GFramework.Cqrs.Abstractions/Directory.Build.props b/GFramework.Cqrs.Abstractions/Directory.Build.props
index 8febdc42..9f372d0c 100644
--- a/GFramework.Cqrs.Abstractions/Directory.Build.props
+++ b/GFramework.Cqrs.Abstractions/Directory.Build.props
@@ -6,11 +6,11 @@
preview
-
+
all
runtime; build; native; contentfiles; analyzers
-
+
all
runtime; build; native; contentfiles; analyzers
From 28cdf791dfed3274ba7e3193d33d055acc697297 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:27:14 +0800
Subject: [PATCH 05/16] =?UTF-8?q?refactor(tests):=20=E7=A7=BB=E9=99=A4?=
=?UTF-8?q?=E9=A1=B9=E7=9B=AE=E8=AD=A6=E5=91=8A=E7=BA=A7=E5=88=AB=E9=85=8D?=
=?UTF-8?q?=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除了 GFramework.Cqrs.Tests 项目的 WarningLevel 配置
- 使测试项目遵循默认警告级别设置
---
GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj | 1 -
1 file changed, 1 deletion(-)
diff --git a/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj b/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
index acbcc536..09cded18 100644
--- a/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
+++ b/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
@@ -7,7 +7,6 @@
enable
false
true
- 0
From aba304f6675a12fb9c4c482f33fb045813025554 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:28:51 +0800
Subject: [PATCH 06/16] =?UTF-8?q?test(cqrs):=20=E6=B7=BB=E5=8A=A0CQRS?=
=?UTF-8?q?=E5=A4=84=E7=90=86=E5=99=A8=E6=B3=A8=E5=86=8C=E5=99=A8=E5=8D=95?=
=?UTF-8?q?=E5=85=83=E6=B5=8B=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 验证通知处理器按稳定名称顺序执行而非依赖反射枚举顺序
- 测试部分类型加载失败时保留可加载类型并记录诊断日志
- 验证源码生成注册器优先级高于反射扫描机制
- 测试生成注册器元数据损坏时回退到反射扫描路径
- 实现确定性通知处理器执行顺序验证功能
- 添加捕获型日志工厂提供程序用于测试断言
- 修正命名空间从Core.Tests.Logging到Cqrs.Tests.Logging
---
GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs | 8 ++++----
GFramework.Cqrs.Tests/Logging/TestLogger.cs | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
index 05a8b0c7..8c3aa9f7 100644
--- a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
+++ b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
@@ -3,7 +3,7 @@ using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
-using GFramework.Core.Tests.Logging;
+using GFramework.Cqrs.Tests.Logging;
namespace GFramework.Core.Tests.Cqrs;
@@ -13,9 +13,6 @@ namespace GFramework.Core.Tests.Cqrs;
[TestFixture]
internal sealed class CqrsHandlerRegistrarTests
{
- private MicrosoftDiContainer? _container;
- private ArchitectureContext? _context;
-
///
/// 初始化测试容器并重置共享状态。
///
@@ -45,6 +42,9 @@ internal sealed class CqrsHandlerRegistrarTests
DeterministicNotificationHandlerState.Reset();
}
+ private MicrosoftDiContainer? _container;
+ private ArchitectureContext? _context;
+
///
/// 验证自动扫描到的通知处理器会按稳定名称顺序执行,而不是依赖反射枚举顺序。
///
diff --git a/GFramework.Cqrs.Tests/Logging/TestLogger.cs b/GFramework.Cqrs.Tests/Logging/TestLogger.cs
index aaf65d22..c0432bc7 100644
--- a/GFramework.Cqrs.Tests/Logging/TestLogger.cs
+++ b/GFramework.Cqrs.Tests/Logging/TestLogger.cs
@@ -14,7 +14,7 @@
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Logging;
-namespace GFramework.Core.Tests.Logging;
+namespace GFramework.Cqrs.Tests.Logging;
///
/// 供 CQRS 测试项目复用的最小日志记录器实现。
From ede8a8faa49479030bc82e970b4a11cdbc2dc3e4 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:34:14 +0800
Subject: [PATCH 07/16] =?UTF-8?q?fix(namespace):=20=E4=BF=AE=E6=AD=A3?=
=?UTF-8?q?=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 修正Core模块命名空间
- 修正Godot模块命名空间
---
.../Architectures/IArchitectureContext.cs | 11 +++++----
.../Cqrs/ICqrsRuntime.cs | 1 +
...ArchitectureAdditionalCqrsHandlersTests.cs | 7 +++---
.../ArchitectureModulesBehaviorTests.cs | 12 +++++-----
.../ArchitectureServicesTests.cs | 20 ++++++++--------
.../Architectures/GameContextTests.cs | 12 ++++++----
.../Command/AbstractAsyncCommandTests.cs | 4 ++--
.../Command/CommandExecutorTests.cs | 4 ++--
.../Cqrs/ContainerRegistrationFixtures.cs | 2 +-
GFramework.Core.Tests/CqrsTestRuntime.cs | 1 +
.../Ioc/MicrosoftDiContainerTests.cs | 2 +-
.../Query/AbstractAsyncQueryTests.cs | 4 ++--
.../Query/AsyncQueryExecutorTests.cs | 4 ++--
.../Query/QueryExecutorTests.cs | 4 ++--
.../Architectures/ArchitectureContext.cs | 11 +++++----
.../Command/AbstractAsyncCommandWithInput.cs | 4 ++--
.../Command/AbstractAsyncCommandWithResult.cs | 4 ++--
.../Command/AbstractCommandWithInput.cs | 10 ++++----
.../Command/AbstractCommandWithResult.cs | 10 ++++----
GFramework.Core/Command/EmptyCommandInput.cs | 4 ++--
.../Extensions/CqrsCoroutineExtensions.cs | 5 ++--
.../Extensions/MediatorCoroutineExtensions.cs | 5 ++--
.../Cqrs/Behaviors/LoggingBehavior.cs | 2 +-
.../Cqrs/Behaviors/PerformanceBehavior.cs | 2 +-
.../Cqrs/Command/AbstractCommandHandler.cs | 4 ++--
.../Command/AbstractStreamCommandHandler.cs | 4 ++--
GFramework.Core/Cqrs/Command/CommandBase.cs | 2 +-
.../Cqrs/Internal/CqrsDispatcher.cs | 1 +
.../Cqrs/Internal/CqrsHandlerRegistrar.cs | 1 +
.../Internal/DefaultCqrsHandlerRegistrar.cs | 2 +-
.../AbstractNotificationHandler.cs | 2 +-
.../Cqrs/Notification/NotificationBase.cs | 4 ++--
.../Cqrs/Query/AbstractQueryHandler.cs | 4 ++--
.../Cqrs/Query/AbstractStreamQueryHandler.cs | 4 ++--
GFramework.Core/Cqrs/Query/QueryBase.cs | 2 +-
.../Cqrs/Request/AbstractRequestHandler.cs | 2 +-
.../Request/AbstractStreamRequestHandler.cs | 2 +-
GFramework.Core/Cqrs/Request/RequestBase.cs | 4 ++--
.../ContextAwareCqrsCommandExtensions.cs | 4 ++--
.../Extensions/ContextAwareCqrsExtensions.cs | 4 ++--
.../ContextAwareCqrsQueryExtensions.cs | 4 ++--
.../ContextAwareMediatorCommandExtensions.cs | 5 ++--
.../ContextAwareMediatorExtensions.cs | 5 ++--
.../ContextAwareMediatorQueryExtensions.cs | 5 ++--
GFramework.Core/Ioc/MicrosoftDiContainer.cs | 2 +-
.../Query/AbstractAsyncQueryWithResult.cs | 6 ++---
.../Query/AbstractQueryWithResult.cs | 8 +++----
GFramework.Core/Query/EmptyQueryInput.cs | 4 ++--
.../Services/Modules/CqrsRuntimeModule.cs | 1 +
.../Cqrs/Command/ICommand.cs | 2 +-
.../Cqrs/Command/ICommandInput.cs | 2 +-
.../Cqrs/ICqrsHandlerRegistrar.cs | 2 +-
GFramework.Cqrs.Abstractions/Cqrs/IInput.cs | 2 +-
.../Cqrs/INotification.cs | 2 +-
.../Cqrs/INotificationHandler.cs | 2 +-
.../Cqrs/IPipelineBehavior.cs | 2 +-
GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs | 2 +-
.../Cqrs/IRequestHandler.cs | 2 +-
.../Cqrs/IStreamRequest.cs | 2 +-
.../Cqrs/IStreamRequestHandler.cs | 2 +-
.../Cqrs/MessageHandlerDelegate.cs | 2 +-
.../Cqrs/Notification/INotificationInput.cs | 2 +-
.../Cqrs/Query/IQuery.cs | 2 +-
.../Cqrs/Query/IQueryInput.cs | 2 +-
.../Cqrs/Request/IRequestInput.cs | 2 +-
GFramework.Cqrs.Abstractions/Cqrs/Unit.cs | 2 +-
.../Coroutine/CqrsCoroutineExtensionsTests.cs | 5 ++--
.../Cqrs/CqrsHandlerRegistrarTests.cs | 3 ++-
GFramework.Cqrs.Tests/CqrsTestRuntime.cs | 3 ++-
.../Mediator/MediatorAdvancedFeaturesTests.cs | 12 +++++-----
.../MediatorArchitectureIntegrationTests.cs | 14 +++++------
.../Mediator/MediatorComprehensiveTests.cs | 23 +++++++++----------
...utoRegisterExportedCollectionsAttribute.cs | 2 +-
.../UI/AutoSceneAttribute.cs | 2 +-
.../UI/AutoUiPageAttribute.cs | 2 +-
.../UI/RegisterExportedCollectionAttribute.cs | 2 +-
.../ContextAwareCoroutineExtensions.cs | 10 ++++----
77 files changed, 176 insertions(+), 167 deletions(-)
diff --git a/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs b/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs
index b9b5dc9a..0eb6a43f 100644
--- a/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs
+++ b/GFramework.Core.Abstractions/Architectures/IArchitectureContext.cs
@@ -1,11 +1,11 @@
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 GFramework.Cqrs.Abstractions.Cqrs;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Abstractions.Architectures;
@@ -131,7 +131,7 @@ public interface IArchitectureContext
///
/// 这是迁移后的推荐命令入口。无返回值命令应实现 IRequest<Unit>,并优先通过 调用。
///
- TResponse SendCommand(Cqrs.Command.ICommand command);
+ TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command);
///
@@ -147,7 +147,8 @@ public interface IArchitectureContext
/// 要发送的 CQRS 命令。
/// 取消令牌。
/// 包含命令执行结果的值任务。
- ValueTask SendCommandAsync(Cqrs.Command.ICommand command,
+ ValueTask SendCommandAsync(
+ GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default);
@@ -176,7 +177,7 @@ public interface IArchitectureContext
///
/// 这是迁移后的推荐查询入口。新查询应优先实现 GFramework.Core.Abstractions.Cqrs.Query.IQuery<TResponse>。
///
- TResponse SendQuery(Cqrs.Query.IQuery query);
+ TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query);
///
/// 异步发送一个旧版查询请求。
@@ -193,7 +194,7 @@ public interface IArchitectureContext
/// 要发送的 CQRS 查询。
/// 取消令牌。
/// 包含查询结果的值任务。
- ValueTask SendQueryAsync(Cqrs.Query.IQuery query,
+ ValueTask SendQueryAsync(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default);
///
diff --git a/GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs b/GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs
index e26310d7..d9efcb07 100644
--- a/GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs
+++ b/GFramework.Core.Abstractions/Cqrs/ICqrsRuntime.cs
@@ -1,4 +1,5 @@
using GFramework.Core.Abstractions.Architectures;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Abstractions.Cqrs;
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.cs
index 04ee0571..67255b41 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureAdditionalCqrsHandlersTests.cs
@@ -3,6 +3,7 @@ using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Architectures;
@@ -12,8 +13,6 @@ namespace GFramework.Core.Tests.Architectures;
[TestFixture]
public sealed class ArchitectureAdditionalCqrsHandlersTests
{
- private ILoggerFactoryProvider? _previousLoggerFactoryProvider;
-
///
/// 初始化日志工厂和共享测试状态。
///
@@ -39,6 +38,8 @@ public sealed class ArchitectureAdditionalCqrsHandlersTests
"LoggerFactoryResolver.Provider should be captured during setup.");
}
+ private ILoggerFactoryProvider? _previousLoggerFactoryProvider;
+
///
/// 验证显式声明的额外程序集会在初始化阶段接入当前架构容器。
///
@@ -197,4 +198,4 @@ internal sealed class AdditionalAssemblyNotificationHandlerRegistry : ICqrsHandl
});
return handler.Object;
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
index ade4a1a7..cfe0db79 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureModulesBehaviorTests.cs
@@ -2,7 +2,7 @@ using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Utility;
using GFramework.Core.Architectures;
using GFramework.Core.Logging;
-using GfCqrs = GFramework.Core.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Architectures;
@@ -151,14 +151,14 @@ public class ArchitectureModulesBehaviorTests
///
/// 用于验证管道行为注册是否生效的测试请求。
///
-public sealed class ModuleBehaviorRequest : GfCqrs.IRequest
+public sealed class ModuleBehaviorRequest : IRequest
{
}
///
/// 处理测试请求的处理器。
///
-public sealed class ModuleBehaviorRequestHandler : GfCqrs.IRequestHandler
+public sealed class ModuleBehaviorRequestHandler : IRequestHandler
{
///
/// 返回固定结果,便于聚焦验证管道行为是否执行。
@@ -177,8 +177,8 @@ public sealed class ModuleBehaviorRequestHandler : GfCqrs.IRequestHandler
/// 请求类型。
/// 响应类型。
-public sealed class TrackingPipelineBehavior : GfCqrs.IPipelineBehavior
- where TRequest : GfCqrs.IRequest
+public sealed class TrackingPipelineBehavior : IPipelineBehavior
+ where TRequest : IRequest
{
///
/// 获取当前测试进程中该请求类型对应的行为触发次数。
@@ -193,7 +193,7 @@ public sealed class TrackingPipelineBehavior : GfCqrs.IPipe
/// 取消令牌。
/// 下游处理器的响应结果。
public async ValueTask Handle(
- TRequest message, GfCqrs.MessageHandlerDelegate next,
+ TRequest message, MessageHandlerDelegate next,
CancellationToken cancellationToken)
{
InvocationCount++;
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
index 94b749b6..57dc19a5 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
@@ -1,6 +1,5 @@
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Command;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Abstractions.Events;
using GFramework.Core.Abstractions.Ioc;
@@ -14,6 +13,7 @@ using GFramework.Core.Environment;
using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Query;
+using GFramework.Cqrs.Abstractions.Cqrs;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Tests.Architectures;
@@ -34,10 +34,6 @@ namespace GFramework.Core.Tests.Architectures;
[TestFixture]
public class ArchitectureServicesTests
{
- private TestArchitectureContextV3? _context;
-
- private ArchitectureServices? _services;
-
[SetUp]
public void SetUp()
{
@@ -45,6 +41,10 @@ public class ArchitectureServicesTests
_context = new TestArchitectureContextV3();
}
+ private TestArchitectureContextV3? _context;
+
+ private ArchitectureServices? _services;
+
private void RegisterBuiltInServices()
{
_services!.ModuleManager.RegisterBuiltInModules(_services.Container);
@@ -359,24 +359,26 @@ public class TestArchitectureContextV3 : IArchitectureContext
throw new NotImplementedException();
}
- public ValueTask SendCommandAsync(Abstractions.Cqrs.Command.ICommand command,
+ public ValueTask SendCommandAsync(
+ GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
- public TResponse SendCommand(Abstractions.Cqrs.Command.ICommand command)
+ public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command)
{
throw new NotImplementedException();
}
- public ValueTask SendQueryAsync(Abstractions.Cqrs.Query.IQuery query,
+ public ValueTask SendQueryAsync(
+ GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
}
- public TResponse SendQuery(Abstractions.Cqrs.Query.IQuery query)
+ public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query)
{
throw new NotImplementedException();
}
diff --git a/GFramework.Core.Tests/Architectures/GameContextTests.cs b/GFramework.Core.Tests/Architectures/GameContextTests.cs
index 28e3cc97..9b990e78 100644
--- a/GFramework.Core.Tests/Architectures/GameContextTests.cs
+++ b/GFramework.Core.Tests/Architectures/GameContextTests.cs
@@ -1,6 +1,5 @@
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Command;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Environment;
using GFramework.Core.Abstractions.Events;
using GFramework.Core.Abstractions.Ioc;
@@ -14,6 +13,7 @@ using GFramework.Core.Environment;
using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Query;
+using GFramework.Cqrs.Abstractions.Cqrs;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Tests.Architectures;
@@ -428,7 +428,8 @@ public class TestArchitectureContext : IArchitectureContext
/// 取消令牌。
/// 命令响应任务。
/// 该测试桩未实现此成员。
- public ValueTask SendCommandAsync(Abstractions.Cqrs.Command.ICommand command,
+ public ValueTask SendCommandAsync(
+ GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
@@ -441,7 +442,7 @@ public class TestArchitectureContext : IArchitectureContext
/// 要发送的命令。
/// 命令响应。
/// 该测试桩未实现此成员。
- public TResponse SendCommand(Abstractions.Cqrs.Command.ICommand command)
+ public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command)
{
throw new NotImplementedException();
}
@@ -454,7 +455,8 @@ public class TestArchitectureContext : IArchitectureContext
/// 取消令牌。
/// 查询结果任务。
/// 该测试桩未实现此成员。
- public ValueTask SendQueryAsync(Abstractions.Cqrs.Query.IQuery query,
+ public ValueTask SendQueryAsync(
+ GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default)
{
throw new NotImplementedException();
@@ -467,7 +469,7 @@ public class TestArchitectureContext : IArchitectureContext
/// 要发送的查询。
/// 查询结果。
/// 该测试桩未实现此成员。
- public TResponse SendQuery(Abstractions.Cqrs.Query.IQuery query)
+ public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query)
{
throw new NotImplementedException();
}
diff --git a/GFramework.Core.Tests/Command/AbstractAsyncCommandTests.cs b/GFramework.Core.Tests/Command/AbstractAsyncCommandTests.cs
index e7a0611e..2cc2ec13 100644
--- a/GFramework.Core.Tests/Command/AbstractAsyncCommandTests.cs
+++ b/GFramework.Core.Tests/Command/AbstractAsyncCommandTests.cs
@@ -1,5 +1,4 @@
using GFramework.Core.Abstractions.Command;
-using GFramework.Core.Abstractions.Cqrs.Command;
using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Architectures;
using GFramework.Core.Command;
@@ -7,6 +6,7 @@ using GFramework.Core.Environment;
using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Query;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Tests.Command;
@@ -396,4 +396,4 @@ public sealed class TestAsyncCommandWithResultChildV3 : AbstractAsyncCommand
public int DoubleValue { get; init; }
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Tests/Query/AsyncQueryExecutorTests.cs b/GFramework.Core.Tests/Query/AsyncQueryExecutorTests.cs
index 6491946d..b23cc8f9 100644
--- a/GFramework.Core.Tests/Query/AsyncQueryExecutorTests.cs
+++ b/GFramework.Core.Tests/Query/AsyncQueryExecutorTests.cs
@@ -1,5 +1,5 @@
-using GFramework.Core.Abstractions.Cqrs.Query;
using GFramework.Core.Query;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Tests.Query;
@@ -292,4 +292,4 @@ public sealed class TestAsyncQueryResult
/// 获取或设置双倍值
///
public int DoubleValue { get; init; }
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core.Tests/Query/QueryExecutorTests.cs b/GFramework.Core.Tests/Query/QueryExecutorTests.cs
index f0305b4b..a9cde117 100644
--- a/GFramework.Core.Tests/Query/QueryExecutorTests.cs
+++ b/GFramework.Core.Tests/Query/QueryExecutorTests.cs
@@ -1,5 +1,5 @@
-using GFramework.Core.Abstractions.Cqrs.Query;
using GFramework.Core.Query;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Tests.Query;
@@ -121,4 +121,4 @@ public sealed class TestStringQuery : AbstractQuery
{
return $"Result: {input.Value * 2}";
}
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Architectures/ArchitectureContext.cs b/GFramework.Core/Architectures/ArchitectureContext.cs
index 63bb34e8..e52960bf 100644
--- a/GFramework.Core/Architectures/ArchitectureContext.cs
+++ b/GFramework.Core/Architectures/ArchitectureContext.cs
@@ -9,6 +9,7 @@ using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Query;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility;
+using GFramework.Cqrs.Abstractions.Cqrs;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Architectures;
@@ -160,7 +161,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 查询响应类型
/// 要发送的查询对象
/// 查询结果
- public TResponse SendQuery(Abstractions.Cqrs.Query.IQuery query)
+ public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query)
{
return SendQueryAsync(query).AsTask().GetAwaiter().GetResult();
}
@@ -186,7 +187,8 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 要发送的查询对象
/// 取消令牌,用于取消操作
/// 包含查询结果的ValueTask
- public async ValueTask SendQueryAsync(Abstractions.Cqrs.Query.IQuery query,
+ public async ValueTask SendQueryAsync(
+ GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(query);
@@ -322,7 +324,8 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 要发送的命令对象
/// 取消令牌,用于取消操作
/// 包含命令执行结果的ValueTask
- public async ValueTask SendCommandAsync(Abstractions.Cqrs.Command.ICommand command,
+ public async ValueTask SendCommandAsync(
+ GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(command);
@@ -361,7 +364,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
/// 命令响应类型
/// 要发送的命令对象
/// 命令执行结果
- public TResponse SendCommand(Abstractions.Cqrs.Command.ICommand command)
+ public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command)
{
return SendCommandAsync(command).AsTask().GetAwaiter().GetResult();
}
diff --git a/GFramework.Core/Command/AbstractAsyncCommandWithInput.cs b/GFramework.Core/Command/AbstractAsyncCommandWithInput.cs
index 491bbd8e..d97ab90b 100644
--- a/GFramework.Core/Command/AbstractAsyncCommandWithInput.cs
+++ b/GFramework.Core/Command/AbstractAsyncCommandWithInput.cs
@@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.Command;
-using GFramework.Core.Abstractions.Cqrs.Command;
using GFramework.Core.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Command;
@@ -26,4 +26,4 @@ public abstract class AbstractAsyncCommand(TInput input) : ContextAwareB
/// 命令输入参数
/// 表示异步操作的任务
protected abstract Task OnExecuteAsync(TInput input);
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Command/AbstractAsyncCommandWithResult.cs b/GFramework.Core/Command/AbstractAsyncCommandWithResult.cs
index 47390192..ae19458e 100644
--- a/GFramework.Core/Command/AbstractAsyncCommandWithResult.cs
+++ b/GFramework.Core/Command/AbstractAsyncCommandWithResult.cs
@@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.Command;
-using GFramework.Core.Abstractions.Cqrs.Command;
using GFramework.Core.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Command;
@@ -27,4 +27,4 @@ public abstract class AbstractAsyncCommand(TInput input) : Cont
/// 命令输入参数
/// 表示异步操作且包含结果的任务
protected abstract Task OnExecuteAsync(TInput input);
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Command/AbstractCommandWithInput.cs b/GFramework.Core/Command/AbstractCommandWithInput.cs
index 7512c760..c3ebbb03 100644
--- a/GFramework.Core/Command/AbstractCommandWithInput.cs
+++ b/GFramework.Core/Command/AbstractCommandWithInput.cs
@@ -1,6 +1,6 @@
-using GFramework.Core.Abstractions.Command;
-using GFramework.Core.Abstractions.Cqrs.Command;
-using GFramework.Core.Rule;
+using GFramework.Core.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
+using ICommand = GFramework.Core.Abstractions.Command.ICommand;
namespace GFramework.Core.Command;
@@ -9,13 +9,13 @@ namespace GFramework.Core.Command;
///
/// 命令输入参数类型,必须实现 ICommandInput 接口
/// 命令执行所需的输入参数
-public abstract class AbstractCommand(TInput input) : ContextAwareBase, GFramework.Core.Abstractions.Command.ICommand
+public abstract class AbstractCommand(TInput input) : ContextAwareBase, ICommand
where TInput : ICommandInput
{
///
/// 执行命令的入口方法,实现 ICommand 接口的 Execute 方法
///
- void GFramework.Core.Abstractions.Command.ICommand.Execute()
+ void ICommand.Execute()
{
OnExecute(input);
}
diff --git a/GFramework.Core/Command/AbstractCommandWithResult.cs b/GFramework.Core/Command/AbstractCommandWithResult.cs
index 7ecc9522..ca40957a 100644
--- a/GFramework.Core/Command/AbstractCommandWithResult.cs
+++ b/GFramework.Core/Command/AbstractCommandWithResult.cs
@@ -1,6 +1,5 @@
-using GFramework.Core.Abstractions.Command;
-using GFramework.Core.Abstractions.Cqrs.Command;
-using GFramework.Core.Rule;
+using GFramework.Core.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Command;
@@ -10,14 +9,15 @@ namespace GFramework.Core.Command;
/// 命令输入参数类型,必须实现 ICommandInput 接口
/// 命令执行后返回的结果类型
/// 命令执行所需的输入参数
-public abstract class AbstractCommand(TInput input) : ContextAwareBase, GFramework.Core.Abstractions.Command.ICommand
+public abstract class AbstractCommand(TInput input)
+ : ContextAwareBase, Abstractions.Command.ICommand
where TInput : ICommandInput
{
///
/// 执行命令的入口方法,实现 ICommand{TResult} 接口的 Execute 方法
///
/// 命令执行后的结果
- TResult GFramework.Core.Abstractions.Command.ICommand.Execute()
+ TResult Abstractions.Command.ICommand.Execute()
{
return OnExecute(input);
}
diff --git a/GFramework.Core/Command/EmptyCommandInput.cs b/GFramework.Core/Command/EmptyCommandInput.cs
index 4d08f4e7..540c158e 100644
--- a/GFramework.Core/Command/EmptyCommandInput.cs
+++ b/GFramework.Core/Command/EmptyCommandInput.cs
@@ -1,4 +1,4 @@
-using GFramework.Core.Abstractions.Cqrs.Command;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Command;
@@ -9,4 +9,4 @@ namespace GFramework.Core.Command;
/// 该类实现了ICommandInput接口,作为命令模式中的输入参数载体
/// 通常用于不需要额外输入参数的简单命令操作
///
-public sealed class EmptyCommandInput : ICommandInput;
\ No newline at end of file
+public sealed class EmptyCommandInput : ICommandInput;
diff --git a/GFramework.Core/Coroutine/Extensions/CqrsCoroutineExtensions.cs b/GFramework.Core/Coroutine/Extensions/CqrsCoroutineExtensions.cs
index 98667656..ec2b55a0 100644
--- a/GFramework.Core/Coroutine/Extensions/CqrsCoroutineExtensions.cs
+++ b/GFramework.Core/Coroutine/Extensions/CqrsCoroutineExtensions.cs
@@ -1,10 +1,9 @@
using System.Runtime.ExceptionServices;
using GFramework.Core.Abstractions.Coroutine;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Rule;
-using GFramework.Core.Coroutine.Extensions;
+using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Cqrs.Extensions;
+namespace GFramework.Core.Coroutine.Extensions;
///
/// 提供 CQRS 命令与协程集成的扩展方法。
diff --git a/GFramework.Core/Coroutine/Extensions/MediatorCoroutineExtensions.cs b/GFramework.Core/Coroutine/Extensions/MediatorCoroutineExtensions.cs
index f955f175..70e3c29f 100644
--- a/GFramework.Core/Coroutine/Extensions/MediatorCoroutineExtensions.cs
+++ b/GFramework.Core/Coroutine/Extensions/MediatorCoroutineExtensions.cs
@@ -13,15 +13,14 @@
using System.ComponentModel;
using GFramework.Core.Abstractions.Coroutine;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Rule;
-using GFramework.Core.Cqrs.Extensions;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Coroutine.Extensions;
///
/// 提供 CQRS 命令与协程集成的扩展方法。
-/// 该类型保留旧名称以兼容历史调用点;新代码应改用 。
+/// 该类型保留旧名称以兼容历史调用点;新代码应改用 。
/// 兼容层计划在未来的 major 版本中移除,因此不会继续承载新能力。
///
[EditorBrowsable(EditorBrowsableState.Never)]
diff --git a/GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs b/GFramework.Core/Cqrs/Behaviors/LoggingBehavior.cs
index 4aaf797a..7230f53d 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 GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Behaviors;
diff --git a/GFramework.Core/Cqrs/Behaviors/PerformanceBehavior.cs b/GFramework.Core/Cqrs/Behaviors/PerformanceBehavior.cs
index 7fc266d0..35ab2978 100644
--- a/GFramework.Core/Cqrs/Behaviors/PerformanceBehavior.cs
+++ b/GFramework.Core/Cqrs/Behaviors/PerformanceBehavior.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 GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Behaviors;
diff --git a/GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs b/GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs
index 528de106..d7ebb117 100644
--- a/GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs
+++ b/GFramework.Core/Cqrs/Command/AbstractCommandHandler.cs
@@ -11,9 +11,9 @@
// 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 GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Cqrs.Command;
diff --git a/GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs b/GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs
index 847563c4..223a9cc5 100644
--- a/GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs
+++ b/GFramework.Core/Cqrs/Command/AbstractStreamCommandHandler.cs
@@ -11,9 +11,9 @@
// 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 GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Cqrs.Command;
diff --git a/GFramework.Core/Cqrs/Command/CommandBase.cs b/GFramework.Core/Cqrs/Command/CommandBase.cs
index 78fa134e..d8232608 100644
--- a/GFramework.Core/Cqrs/Command/CommandBase.cs
+++ b/GFramework.Core/Cqrs/Command/CommandBase.cs
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Abstractions.Cqrs.Command;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Cqrs.Command;
diff --git a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs b/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
index f1950cf7..4e2c739d 100644
--- a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
+++ b/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
@@ -5,6 +5,7 @@ using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Internal;
diff --git a/GFramework.Core/Cqrs/Internal/CqrsHandlerRegistrar.cs b/GFramework.Core/Cqrs/Internal/CqrsHandlerRegistrar.cs
index 189ae8f0..65b1cbcf 100644
--- a/GFramework.Core/Cqrs/Internal/CqrsHandlerRegistrar.cs
+++ b/GFramework.Core/Cqrs/Internal/CqrsHandlerRegistrar.cs
@@ -2,6 +2,7 @@ using System.Reflection;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Internal;
diff --git a/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs b/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
index dca76290..ddf0c06a 100644
--- a/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
+++ b/GFramework.Core/Cqrs/Internal/DefaultCqrsHandlerRegistrar.cs
@@ -1,7 +1,7 @@
using System.Reflection;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Internal;
diff --git a/GFramework.Core/Cqrs/Notification/AbstractNotificationHandler.cs b/GFramework.Core/Cqrs/Notification/AbstractNotificationHandler.cs
index 1b1157ab..da6f5281 100644
--- a/GFramework.Core/Cqrs/Notification/AbstractNotificationHandler.cs
+++ b/GFramework.Core/Cqrs/Notification/AbstractNotificationHandler.cs
@@ -12,7 +12,7 @@
// limitations under the License.
using GFramework.Core.Rule;
-using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Notification;
diff --git a/GFramework.Core/Cqrs/Notification/NotificationBase.cs b/GFramework.Core/Cqrs/Notification/NotificationBase.cs
index f04488b9..05db2de7 100644
--- a/GFramework.Core/Cqrs/Notification/NotificationBase.cs
+++ b/GFramework.Core/Cqrs/Notification/NotificationBase.cs
@@ -11,8 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Abstractions.Cqrs.Notification;
-using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs.Notification;
namespace GFramework.Core.Cqrs.Notification;
diff --git a/GFramework.Core/Cqrs/Query/AbstractQueryHandler.cs b/GFramework.Core/Cqrs/Query/AbstractQueryHandler.cs
index e9a3795f..85c86425 100644
--- a/GFramework.Core/Cqrs/Query/AbstractQueryHandler.cs
+++ b/GFramework.Core/Cqrs/Query/AbstractQueryHandler.cs
@@ -11,9 +11,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Abstractions.Cqrs;
-using GFramework.Core.Abstractions.Cqrs.Query;
using GFramework.Core.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Cqrs.Query;
diff --git a/GFramework.Core/Cqrs/Query/AbstractStreamQueryHandler.cs b/GFramework.Core/Cqrs/Query/AbstractStreamQueryHandler.cs
index 015da1da..9695dc42 100644
--- a/GFramework.Core/Cqrs/Query/AbstractStreamQueryHandler.cs
+++ b/GFramework.Core/Cqrs/Query/AbstractStreamQueryHandler.cs
@@ -12,8 +12,8 @@
// limitations under the License.
using GFramework.Core.Rule;
-using GFramework.Core.Abstractions.Cqrs;
-using GFramework.Core.Abstractions.Cqrs.Query;
+using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Cqrs.Query;
diff --git a/GFramework.Core/Cqrs/Query/QueryBase.cs b/GFramework.Core/Cqrs/Query/QueryBase.cs
index 6bccf549..759b8df1 100644
--- a/GFramework.Core/Cqrs/Query/QueryBase.cs
+++ b/GFramework.Core/Cqrs/Query/QueryBase.cs
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Abstractions.Cqrs.Query;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Cqrs.Query;
diff --git a/GFramework.Core/Cqrs/Request/AbstractRequestHandler.cs b/GFramework.Core/Cqrs/Request/AbstractRequestHandler.cs
index 4ef6a270..f26c7cfa 100644
--- a/GFramework.Core/Cqrs/Request/AbstractRequestHandler.cs
+++ b/GFramework.Core/Cqrs/Request/AbstractRequestHandler.cs
@@ -12,7 +12,7 @@
// limitations under the License.
using GFramework.Core.Rule;
-using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Request;
diff --git a/GFramework.Core/Cqrs/Request/AbstractStreamRequestHandler.cs b/GFramework.Core/Cqrs/Request/AbstractStreamRequestHandler.cs
index a15ed5d7..2cbf438d 100644
--- a/GFramework.Core/Cqrs/Request/AbstractStreamRequestHandler.cs
+++ b/GFramework.Core/Cqrs/Request/AbstractStreamRequestHandler.cs
@@ -12,7 +12,7 @@
// limitations under the License.
using GFramework.Core.Rule;
-using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Request;
diff --git a/GFramework.Core/Cqrs/Request/RequestBase.cs b/GFramework.Core/Cqrs/Request/RequestBase.cs
index ce85784f..5ff18a04 100644
--- a/GFramework.Core/Cqrs/Request/RequestBase.cs
+++ b/GFramework.Core/Cqrs/Request/RequestBase.cs
@@ -11,8 +11,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using GFramework.Core.Abstractions.Cqrs.Request;
-using GFramework.Core.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs.Request;
namespace GFramework.Core.Cqrs.Request;
diff --git a/GFramework.Core/Extensions/ContextAwareCqrsCommandExtensions.cs b/GFramework.Core/Extensions/ContextAwareCqrsCommandExtensions.cs
index b71669ee..f99fae25 100644
--- a/GFramework.Core/Extensions/ContextAwareCqrsCommandExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareCqrsCommandExtensions.cs
@@ -1,7 +1,7 @@
-using GFramework.Core.Abstractions.Cqrs.Command;
using GFramework.Core.Abstractions.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
-namespace GFramework.Core.Cqrs.Extensions;
+namespace GFramework.Core.Extensions;
///
/// 提供对 接口的 CQRS 命令扩展方法。
diff --git a/GFramework.Core/Extensions/ContextAwareCqrsExtensions.cs b/GFramework.Core/Extensions/ContextAwareCqrsExtensions.cs
index ab09e689..6db156f5 100644
--- a/GFramework.Core/Extensions/ContextAwareCqrsExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareCqrsExtensions.cs
@@ -1,7 +1,7 @@
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Cqrs.Extensions;
+namespace GFramework.Core.Extensions;
///
/// 提供对 接口的 CQRS 统一扩展方法。
diff --git a/GFramework.Core/Extensions/ContextAwareCqrsQueryExtensions.cs b/GFramework.Core/Extensions/ContextAwareCqrsQueryExtensions.cs
index 9906bc3d..40ec0f7d 100644
--- a/GFramework.Core/Extensions/ContextAwareCqrsQueryExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareCqrsQueryExtensions.cs
@@ -1,7 +1,7 @@
-using GFramework.Core.Abstractions.Cqrs.Query;
using GFramework.Core.Abstractions.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
-namespace GFramework.Core.Cqrs.Extensions;
+namespace GFramework.Core.Extensions;
///
/// 提供对 接口的 CQRS 查询扩展方法。
diff --git a/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
index 85f9776e..41d9df27 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
@@ -1,13 +1,12 @@
using System.ComponentModel;
-using GFramework.Core.Abstractions.Cqrs.Command;
using GFramework.Core.Abstractions.Rule;
-using GFramework.Core.Cqrs.Extensions;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Extensions;
///
/// 提供对 接口的 CQRS 命令扩展方法。
-/// 该类型保留旧名称以兼容历史调用点;新代码应改用 。
+/// 该类型保留旧名称以兼容历史调用点;新代码应改用 。
/// 兼容层计划在未来的 major 版本中移除,因此不会继续承载新能力。
///
[EditorBrowsable(EditorBrowsableState.Never)]
diff --git a/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs
index 68b130ce..b4d607bc 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs
@@ -1,13 +1,12 @@
using System.ComponentModel;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Rule;
-using GFramework.Core.Cqrs.Extensions;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Extensions;
///
/// 提供对 接口的 CQRS 统一接口扩展方法。
-/// 该类型保留旧名称以兼容历史调用点;新代码应改用 。
+/// 该类型保留旧名称以兼容历史调用点;新代码应改用 。
/// 兼容层计划在未来的 major 版本中移除,因此不会继续承载新能力。
///
[EditorBrowsable(EditorBrowsableState.Never)]
diff --git a/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
index d7fada4a..49b57445 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
@@ -1,13 +1,12 @@
using System.ComponentModel;
-using GFramework.Core.Abstractions.Cqrs.Query;
using GFramework.Core.Abstractions.Rule;
-using GFramework.Core.Cqrs.Extensions;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Extensions;
///
/// 提供对 接口的 CQRS 查询扩展方法。
-/// 该类型保留旧名称以兼容历史调用点;新代码应改用 。
+/// 该类型保留旧名称以兼容历史调用点;新代码应改用 。
/// 兼容层计划在未来的 major 版本中移除,因此不会继续承载新能力。
///
[EditorBrowsable(EditorBrowsableState.Never)]
diff --git a/GFramework.Core/Ioc/MicrosoftDiContainer.cs b/GFramework.Core/Ioc/MicrosoftDiContainer.cs
index 49ce44f2..dc14485e 100644
--- a/GFramework.Core/Ioc/MicrosoftDiContainer.cs
+++ b/GFramework.Core/Ioc/MicrosoftDiContainer.cs
@@ -1,12 +1,12 @@
using System.ComponentModel;
using System.Reflection;
using GFramework.Core.Abstractions.Bases;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Logging;
using GFramework.Core.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Ioc;
diff --git a/GFramework.Core/Query/AbstractAsyncQueryWithResult.cs b/GFramework.Core/Query/AbstractAsyncQueryWithResult.cs
index 03c35071..03713712 100644
--- a/GFramework.Core/Query/AbstractAsyncQueryWithResult.cs
+++ b/GFramework.Core/Query/AbstractAsyncQueryWithResult.cs
@@ -1,6 +1,6 @@
-using GFramework.Core.Abstractions.Cqrs.Query;
-using GFramework.Core.Abstractions.Query;
+using GFramework.Core.Abstractions.Query;
using GFramework.Core.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Query;
@@ -30,4 +30,4 @@ public abstract class AbstractAsyncQuery(
/// 查询输入参数
/// 返回查询结果的异步任务
protected abstract Task OnDoAsync(TInput input);
-}
\ No newline at end of file
+}
diff --git a/GFramework.Core/Query/AbstractQueryWithResult.cs b/GFramework.Core/Query/AbstractQueryWithResult.cs
index 2c87622a..ae099abe 100644
--- a/GFramework.Core/Query/AbstractQueryWithResult.cs
+++ b/GFramework.Core/Query/AbstractQueryWithResult.cs
@@ -1,6 +1,5 @@
-using GFramework.Core.Abstractions.Cqrs.Query;
-using GFramework.Core.Abstractions.Query;
-using GFramework.Core.Rule;
+using GFramework.Core.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Query;
@@ -9,7 +8,8 @@ namespace GFramework.Core.Query;
///
/// 查询输入参数的类型,必须实现IQueryInput接口
/// 查询结果的类型
-public abstract class AbstractQuery(TInput input) : ContextAwareBase, GFramework.Core.Abstractions.Query.IQuery
+public abstract class AbstractQuery(TInput input)
+ : ContextAwareBase, Abstractions.Query.IQuery
where TInput : IQueryInput
{
///
diff --git a/GFramework.Core/Query/EmptyQueryInput.cs b/GFramework.Core/Query/EmptyQueryInput.cs
index 7d707189..a6a0cd67 100644
--- a/GFramework.Core/Query/EmptyQueryInput.cs
+++ b/GFramework.Core/Query/EmptyQueryInput.cs
@@ -1,4 +1,4 @@
-using GFramework.Core.Abstractions.Cqrs.Query;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Query;
@@ -8,4 +8,4 @@ namespace GFramework.Core.Query;
///
/// 该类实现了IQueryInput接口,作为占位符使用,适用于那些不需要额外输入参数的查询场景
///
-public sealed class EmptyQueryInput : IQueryInput;
\ No newline at end of file
+public sealed class EmptyQueryInput : IQueryInput;
diff --git a/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs b/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
index 915e07e2..5ca7a909 100644
--- a/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
+++ b/GFramework.Core/Services/Modules/CqrsRuntimeModule.cs
@@ -3,6 +3,7 @@ using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Cqrs.Internal;
using GFramework.Core.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Services.Modules;
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs
index ba04331e..6eaa1869 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs.Command;
+namespace GFramework.Cqrs.Abstractions.Cqrs.Command;
///
/// 表示一个 CQRS 命令。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommandInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommandInput.cs
index a00d67e6..9f5be0b6 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommandInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommandInput.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs.Command;
+namespace GFramework.Cqrs.Abstractions.Cqrs.Command;
///
/// 命令输入接口,定义命令模式中输入数据的契约
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs b/GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs
index 0257b3ec..39635d3f 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/ICqrsHandlerRegistrar.cs
@@ -1,6 +1,6 @@
using System.Reflection;
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 定义 CQRS 处理器程序集接入的 runtime seam。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/IInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/IInput.cs
index ddf00622..a8b6dbcb 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/IInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/IInput.cs
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 表示输入数据的标记接口。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/INotification.cs b/GFramework.Cqrs.Abstractions/Cqrs/INotification.cs
index 9d69e28e..727f9c07 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/INotification.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/INotification.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 表示一个一对多发布的通知消息。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/INotificationHandler.cs b/GFramework.Cqrs.Abstractions/Cqrs/INotificationHandler.cs
index 23861d1d..e3a007c8 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/INotificationHandler.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/INotificationHandler.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 表示处理通知消息的处理器契约。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/IPipelineBehavior.cs b/GFramework.Cqrs.Abstractions/Cqrs/IPipelineBehavior.cs
index cd01aad3..11423c40 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/IPipelineBehavior.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/IPipelineBehavior.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 定义 CQRS 请求处理前后的管道行为。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs b/GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs
index 26259fc4..78627e4b 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 表示一个有响应的 CQRS 请求。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/IRequestHandler.cs b/GFramework.Cqrs.Abstractions/Cqrs/IRequestHandler.cs
index 2415e282..95cdd1d1 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/IRequestHandler.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/IRequestHandler.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 表示处理单个 CQRS 请求的处理器契约。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs b/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs
index 05ffa5df..37a211d4 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 表示一个流式 CQRS 请求。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequestHandler.cs b/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequestHandler.cs
index 1c6e02a7..44e7c79d 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequestHandler.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequestHandler.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 表示处理流式 CQRS 请求的处理器契约。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/MessageHandlerDelegate.cs b/GFramework.Cqrs.Abstractions/Cqrs/MessageHandlerDelegate.cs
index 520f9fee..8575ebd8 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/MessageHandlerDelegate.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/MessageHandlerDelegate.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 表示 CQRS 请求在管道中继续向下执行的处理委托。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Notification/INotificationInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Notification/INotificationInput.cs
index 236b30ff..4fb6f735 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/Notification/INotificationInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Notification/INotificationInput.cs
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-namespace GFramework.Core.Abstractions.Cqrs.Notification;
+namespace GFramework.Cqrs.Abstractions.Cqrs.Notification;
///
/// 表示通知输入数据的标记接口。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs
index cbb1586e..9592a9bd 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs.Query;
+namespace GFramework.Cqrs.Abstractions.Cqrs.Query;
///
/// 表示一个 CQRS 查询。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Query/IQueryInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQueryInput.cs
index 7e0a5b4f..a17e7b6a 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/Query/IQueryInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQueryInput.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs.Query;
+namespace GFramework.Cqrs.Abstractions.Cqrs.Query;
///
/// 查询输入接口,定义了查询操作的输入规范
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Request/IRequestInput.cs b/GFramework.Cqrs.Abstractions/Cqrs/Request/IRequestInput.cs
index 7b7ff83c..14f89b89 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/Request/IRequestInput.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Request/IRequestInput.cs
@@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-namespace GFramework.Core.Abstractions.Cqrs.Request;
+namespace GFramework.Cqrs.Abstractions.Cqrs.Request;
///
/// 表示请求输入数据的标记接口。
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Unit.cs b/GFramework.Cqrs.Abstractions/Cqrs/Unit.cs
index 7dc3da14..57d053bc 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/Unit.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Unit.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Core.Abstractions.Cqrs;
+namespace GFramework.Cqrs.Abstractions.Cqrs;
///
/// 表示没有实际返回值的 CQRS 响应类型。
diff --git a/GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs b/GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs
index 67e9537d..bd41c1e4 100644
--- a/GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs
+++ b/GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs
@@ -13,11 +13,10 @@
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Coroutine;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Rule;
-using GFramework.Core.Cqrs.Extensions;
+using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Tests.Coroutine;
+namespace GFramework.Cqrs.Tests.Coroutine;
///
/// 的单元测试类。
diff --git a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
index 8c3aa9f7..360fe97b 100644
--- a/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
+++ b/GFramework.Cqrs.Tests/Cqrs/CqrsHandlerRegistrarTests.cs
@@ -3,9 +3,10 @@ using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Tests.Logging;
-namespace GFramework.Core.Tests.Cqrs;
+namespace GFramework.Cqrs.Tests.Cqrs;
///
/// 验证 CQRS 处理器自动注册在顺序与容错层面的可观察行为。
diff --git a/GFramework.Cqrs.Tests/CqrsTestRuntime.cs b/GFramework.Cqrs.Tests/CqrsTestRuntime.cs
index f9bb143b..7676f143 100644
--- a/GFramework.Cqrs.Tests/CqrsTestRuntime.cs
+++ b/GFramework.Cqrs.Tests/CqrsTestRuntime.cs
@@ -4,8 +4,9 @@ using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Tests;
+namespace GFramework.Cqrs.Tests;
///
/// 为测试项目提供对 CQRS 处理器真实注册入口的受控访问。
diff --git a/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
index cadb340b..257809f0 100644
--- a/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
+++ b/GFramework.Cqrs.Tests/Mediator/MediatorAdvancedFeaturesTests.cs
@@ -1,9 +1,9 @@
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Architectures;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
+using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Core.Tests.Mediator;
+namespace GFramework.Cqrs.Tests.Mediator;
///
/// Mediator高级特性专项测试
@@ -12,10 +12,6 @@ namespace GFramework.Core.Tests.Mediator;
[TestFixture]
public class MediatorAdvancedFeaturesTests
{
- private MicrosoftDiContainer? _container;
-
- private ArchitectureContext? _context;
-
[SetUp]
public void SetUp()
{
@@ -44,6 +40,10 @@ public class MediatorAdvancedFeaturesTests
_container = null;
}
+ private MicrosoftDiContainer? _container;
+
+ private ArchitectureContext? _context;
+
[Test]
public async Task Request_With_Validation_Behavior_Should_Validate_Input()
diff --git a/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
index 7d73a45e..e403735e 100644
--- a/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
+++ b/GFramework.Cqrs.Tests/Mediator/MediatorArchitectureIntegrationTests.cs
@@ -1,13 +1,13 @@
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 GFramework.Core.Rule;
+using GFramework.Cqrs.Abstractions.Cqrs;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
-namespace GFramework.Core.Tests.Mediator;
+namespace GFramework.Cqrs.Tests.Mediator;
///
/// Mediator与架构上下文集成测试
@@ -16,11 +16,6 @@ namespace GFramework.Core.Tests.Mediator;
[TestFixture]
public class MediatorArchitectureIntegrationTests
{
- private CommandExecutor? _commandBus;
- private MicrosoftDiContainer? _container;
-
- private ArchitectureContext? _context;
-
[SetUp]
public void SetUp()
{
@@ -54,6 +49,11 @@ public class MediatorArchitectureIntegrationTests
_commandBus = null;
}
+ private CommandExecutor? _commandBus;
+ private MicrosoftDiContainer? _container;
+
+ private ArchitectureContext? _context;
+
[Test]
public async Task Handler_Can_Access_Architecture_Context()
{
diff --git a/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs b/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs
index cdaf9af6..b0b510d6 100644
--- a/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs
+++ b/GFramework.Cqrs.Tests/Mediator/MediatorComprehensiveTests.cs
@@ -1,5 +1,4 @@
using GFramework.Core.Abstractions.Architectures;
-using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Events;
using GFramework.Core.Architectures;
using GFramework.Core.Command;
@@ -8,23 +7,14 @@ using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Query;
+using GFramework.Cqrs.Abstractions.Cqrs;
using ICommand = GFramework.Core.Abstractions.Command.ICommand;
-using Unit = GFramework.Core.Abstractions.Cqrs.Unit;
-namespace GFramework.Core.Tests.Mediator;
+namespace GFramework.Cqrs.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;
-
///
/// 测试初始化方法,在每个测试方法执行前运行。
/// 负责初始化日志工厂、依赖注入容器、自有 CQRS 处理器以及各种总线服务。
@@ -79,6 +69,15 @@ public class MediatorComprehensiveTests
_environment = null;
}
+ private AsyncQueryExecutor? _asyncQueryBus;
+ private CommandExecutor? _commandBus;
+ private MicrosoftDiContainer? _container;
+
+ private ArchitectureContext? _context;
+ private DefaultEnvironment? _environment;
+ private EventBus? _eventBus;
+ private QueryExecutor? _queryBus;
+
///
/// 测试SendRequestAsync方法在请求有效时返回结果
///
diff --git a/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoRegisterExportedCollectionsAttribute.cs b/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoRegisterExportedCollectionsAttribute.cs
index 1c2699ae..71d523eb 100644
--- a/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoRegisterExportedCollectionsAttribute.cs
+++ b/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoRegisterExportedCollectionsAttribute.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Godot.SourceGenerators.Abstractions;
+namespace GFramework.Godot.SourceGenerators.Abstractions.UI;
///
/// 标记类型允许为带映射特性的导出集合生成批量注册代码。
diff --git a/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoSceneAttribute.cs b/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoSceneAttribute.cs
index 3b73af7b..4e56cd9b 100644
--- a/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoSceneAttribute.cs
+++ b/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoSceneAttribute.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Godot.SourceGenerators.Abstractions;
+namespace GFramework.Godot.SourceGenerators.Abstractions.UI;
///
/// 标记场景根节点类型,Source Generator 会生成场景行为样板代码。
diff --git a/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoUiPageAttribute.cs b/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoUiPageAttribute.cs
index 56957fb8..0356c43c 100644
--- a/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoUiPageAttribute.cs
+++ b/GFramework.Godot.SourceGenerators.Abstractions/UI/AutoUiPageAttribute.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Godot.SourceGenerators.Abstractions;
+namespace GFramework.Godot.SourceGenerators.Abstractions.UI;
///
/// 标记 UI 页面类型,Source Generator 会生成页面行为样板代码。
diff --git a/GFramework.Godot.SourceGenerators.Abstractions/UI/RegisterExportedCollectionAttribute.cs b/GFramework.Godot.SourceGenerators.Abstractions/UI/RegisterExportedCollectionAttribute.cs
index dd809fc4..c4fe14a2 100644
--- a/GFramework.Godot.SourceGenerators.Abstractions/UI/RegisterExportedCollectionAttribute.cs
+++ b/GFramework.Godot.SourceGenerators.Abstractions/UI/RegisterExportedCollectionAttribute.cs
@@ -1,4 +1,4 @@
-namespace GFramework.Godot.SourceGenerators.Abstractions;
+namespace GFramework.Godot.SourceGenerators.Abstractions.UI;
///
/// 声明导出集合应当转发到哪个注册器成员及其方法。
diff --git a/GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs b/GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs
index 98d57936..6805d2e7 100644
--- a/GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs
+++ b/GFramework.Godot/Coroutine/ContextAwareCoroutineExtensions.cs
@@ -1,10 +1,10 @@
-using GFramework.Core.Abstractions.Cqrs;
-using GFramework.Core.Abstractions.Cqrs.Command;
-using GFramework.Core.Abstractions.Cqrs.Query;
-using GFramework.Core.Abstractions.Rule;
+using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Coroutine;
using GFramework.Core.Coroutine.Extensions;
-using GFramework.Core.Cqrs.Extensions;
+using GFramework.Core.Extensions;
+using GFramework.Cqrs.Abstractions.Cqrs;
+using GFramework.Cqrs.Abstractions.Cqrs.Command;
+using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Godot.Coroutine;
From 932235e8ccc806822e5717f90d7bea0d015741b1 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:36:08 +0800
Subject: [PATCH 08/16] =?UTF-8?q?refactor(tests):=20=E6=9B=B4=E6=96=B0Cqrs?=
=?UTF-8?q?CoroutineExtensionsTests=E4=B8=AD=E7=9A=84=E5=91=BD=E5=90=8D?=
=?UTF-8?q?=E7=A9=BA=E9=97=B4=E5=BC=95=E7=94=A8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 添加GFramework.Core.Coroutine.Extensions命名空间引用
- 保持现有测试功能完整性
- 优化代码结构以匹配最新框架变更
---
GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs b/GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs
index bd41c1e4..ccc6e27f 100644
--- a/GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs
+++ b/GFramework.Cqrs.Tests/Coroutine/CqrsCoroutineExtensionsTests.cs
@@ -14,6 +14,7 @@
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Coroutine;
using GFramework.Core.Abstractions.Rule;
+using GFramework.Core.Coroutine.Extensions;
using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Cqrs.Tests.Coroutine;
From e2001766cbb76acb5e2bf50eab02fdf58d43bab3 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:57:08 +0800
Subject: [PATCH 09/16] =?UTF-8?q?feat(arch):=20=E6=B7=BB=E5=8A=A0=E6=9E=B6?=
=?UTF-8?q?=E6=9E=84=E4=B8=8A=E4=B8=8B=E6=96=87=E5=AE=9E=E7=8E=B0=E5=8F=8A?=
=?UTF-8?q?=E5=AE=8C=E6=95=B4=E6=B5=8B=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 实现 ArchitectureContext 类,提供对系统、模型、工具等组件的访问
- 集成 CQRS runtime,支持命令、查询、事件的执行管理
- 添加服务缓存机制,优化容器解析性能
- 实现并发安全的 CQRS runtime 懒加载
- 提供同步和异步的请求处理方法
- 支持优先级排序的服务实例获取
- 添加完整的单元测试覆盖构造函数、查询、命令、事件等功能
- 配置测试项目依赖和全局引用
- 实现共享的 CQRS 测试运行时支持
---
.../Architectures/ArchitectureContextTests.cs | 68 +++++++++-
GFramework.Core.Tests/CqrsTestRuntime.cs | 119 ------------------
.../GFramework.Core.Tests.csproj | 1 +
GFramework.Core.Tests/GlobalUsings.cs | 1 +
.../Architectures/ArchitectureContext.cs | 41 +++++-
.../GFramework.Cqrs.Tests.csproj | 1 +
GFramework.Cqrs.Tests/GlobalUsings.cs | 1 +
.../Shared}/CqrsTestRuntime.cs | 30 ++---
8 files changed, 118 insertions(+), 144 deletions(-)
delete mode 100644 GFramework.Core.Tests/CqrsTestRuntime.cs
rename {GFramework.Cqrs.Tests => tests/Shared}/CqrsTestRuntime.cs (76%)
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
index 00684cb9..4b5da422 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
@@ -1,8 +1,10 @@
using System.Reflection;
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Command;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Enums;
using GFramework.Core.Abstractions.Environment;
+using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Query;
using GFramework.Core.Abstractions.Systems;
@@ -14,6 +16,7 @@ using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Query;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Architectures;
@@ -298,6 +301,69 @@ public class ArchitectureContextTests
Assert.That(environment, Is.Not.Null);
Assert.That(environment, Is.InstanceOf());
}
+
+ ///
+ /// 测试 CQRS runtime 在并发首次访问时只会从容器解析一次。
+ ///
+ [Test]
+ public async Task SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently()
+ {
+ using var startGate = new ManualResetEventSlim(false);
+ using var allowResolutionToComplete = new ManualResetEventSlim(false);
+ var resolutionCallCount = 0;
+ var runtime = new Mock(MockBehavior.Strict);
+ var container = new Mock(MockBehavior.Strict);
+
+ runtime.Setup(mockRuntime => mockRuntime.SendAsync(
+ It.IsAny(),
+ It.IsAny>(),
+ It.IsAny()))
+ .Returns(new ValueTask(42));
+
+ container.Setup(mockContainer => mockContainer.Get())
+ .Returns(() =>
+ {
+ Interlocked.Increment(ref resolutionCallCount);
+ allowResolutionToComplete.Wait();
+ return runtime.Object;
+ });
+
+ var context = new ArchitectureContext(container.Object);
+ var requests = Enumerable.Range(0, 16)
+ .Select(_ => Task.Run(async () =>
+ {
+ startGate.Wait();
+ return await context.SendRequestAsync(new TestCqrsRequest());
+ }))
+ .ToArray();
+
+ startGate.Set();
+
+ Assert.That(
+ SpinWait.SpinUntil(() => Volatile.Read(ref resolutionCallCount) > 0, TimeSpan.FromSeconds(1)),
+ Is.True,
+ "Expected at least one CQRS runtime resolution attempt.");
+
+ // 留出一个短暂窗口,让并发首次访问都在 runtime 尚未发布前抵达同一初始化点。
+ await Task.Delay(50);
+ allowResolutionToComplete.Set();
+
+ var responses = await Task.WhenAll(requests);
+
+ Assert.That(responses, Has.All.EqualTo(42));
+ Assert.That(resolutionCallCount, Is.EqualTo(1));
+ container.Verify(mockContainer => mockContainer.Get(), Times.Once);
+ runtime.Verify(
+ mockRuntime => mockRuntime.SendAsync(
+ It.IsAny(),
+ It.IsAny>(),
+ It.IsAny()),
+ Times.Exactly(requests.Length));
+ }
+
+ private sealed class TestCqrsRequest : IRequest
+ {
+ }
}
#region Test Classes
@@ -442,4 +508,4 @@ public class TestEventV2
public int Data { get; init; }
}
-#endregion
\ No newline at end of file
+#endregion
diff --git a/GFramework.Core.Tests/CqrsTestRuntime.cs b/GFramework.Core.Tests/CqrsTestRuntime.cs
deleted file mode 100644
index 3f8a7813..00000000
--- a/GFramework.Core.Tests/CqrsTestRuntime.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using System.Reflection;
-using GFramework.Core.Abstractions.Cqrs;
-using GFramework.Core.Abstractions.Ioc;
-using GFramework.Core.Abstractions.Logging;
-using GFramework.Core.Architectures;
-using GFramework.Core.Ioc;
-using GFramework.Core.Logging;
-using GFramework.Cqrs.Abstractions.Cqrs;
-
-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.");
-
- private static readonly Type CqrsDispatcherType = typeof(ArchitectureContext).Assembly
- .GetType(
- "GFramework.Core.Cqrs.Internal.CqrsDispatcher",
- throwOnError: true)!
- ?? throw new InvalidOperationException(
- "Failed to locate CqrsDispatcher type.");
-
- private static readonly ConstructorInfo CqrsDispatcherConstructor = CqrsDispatcherType.GetConstructor(
- BindingFlags.Instance |
- BindingFlags.Public |
- BindingFlags.NonPublic,
- binder: null,
- [
- typeof(IIocContainer),
- typeof(ILogger)
- ],
- modifiers: null)
- ?? throw new InvalidOperationException(
- "Failed to locate CqrsDispatcher constructor.");
-
- private static readonly Type DefaultCqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
- .GetType(
- "GFramework.Core.Cqrs.Internal.DefaultCqrsHandlerRegistrar",
- throwOnError: true)!
- ?? throw new InvalidOperationException(
- "Failed to locate DefaultCqrsHandlerRegistrar type.");
-
- private static readonly ConstructorInfo DefaultCqrsHandlerRegistrarConstructor =
- DefaultCqrsHandlerRegistrarType.GetConstructor(
- BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
- binder: null,
- [
- typeof(IIocContainer),
- typeof(ILogger)
- ],
- modifiers: null)
- ?? throw new InvalidOperationException(
- "Failed to locate DefaultCqrsHandlerRegistrar constructor.");
-
- ///
- /// 为裸测试容器补齐默认 CQRS runtime seam。
- /// 这使仅使用 的测试环境也能观察与生产路径一致的 runtime 行为,
- /// 而无需完整启动服务模块管理器。
- ///
- /// 目标测试容器。
- internal static void RegisterInfrastructure(MicrosoftDiContainer container)
- {
- ArgumentNullException.ThrowIfNull(container);
-
- var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
- var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
- var runtime = (ICqrsRuntime)CqrsDispatcherConstructor.Invoke([container, runtimeLogger]);
- var registrar =
- (ICqrsHandlerRegistrar)DefaultCqrsHandlerRegistrarConstructor.Invoke([container, registrarLogger]);
-
- container.Register(runtime);
- container.Register(registrar);
- }
-
- ///
- /// 通过与生产代码一致的注册入口扫描并注册指定程序集中的 CQRS 处理器。
- ///
- /// 承载处理器映射的测试容器。
- /// 要扫描的程序集集合。
- internal static void RegisterHandlers(MicrosoftDiContainer container, params Assembly[] assemblies)
- {
- ArgumentNullException.ThrowIfNull(container);
- ArgumentNullException.ThrowIfNull(assemblies);
-
- RegisterInfrastructure(container);
-
- 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/GFramework.Core.Tests.csproj b/GFramework.Core.Tests/GFramework.Core.Tests.csproj
index 97663fe0..c455f570 100644
--- a/GFramework.Core.Tests/GFramework.Core.Tests.csproj
+++ b/GFramework.Core.Tests/GFramework.Core.Tests.csproj
@@ -18,6 +18,7 @@
+
diff --git a/GFramework.Core.Tests/GlobalUsings.cs b/GFramework.Core.Tests/GlobalUsings.cs
index fe9b7de1..daaaf5b7 100644
--- a/GFramework.Core.Tests/GlobalUsings.cs
+++ b/GFramework.Core.Tests/GlobalUsings.cs
@@ -16,6 +16,7 @@ global using System.Collections.Generic;
global using System.Linq;
global using System.Threading;
global using System.Threading.Tasks;
+global using GFramework.Tests.Shared;
global using NUnit.Framework;
global using NUnit.Compatibility;
global using GFramework.Core.Systems;
diff --git a/GFramework.Core/Architectures/ArchitectureContext.cs b/GFramework.Core/Architectures/ArchitectureContext.cs
index e52960bf..9b6d7dc2 100644
--- a/GFramework.Core/Architectures/ArchitectureContext.cs
+++ b/GFramework.Core/Architectures/ArchitectureContext.cs
@@ -17,19 +17,48 @@ namespace GFramework.Core.Architectures;
///
/// 架构上下文类,提供对系统、模型、工具等组件的访问以及命令、查询、事件的执行管理
///
-public class ArchitectureContext(IIocContainer container) : IArchitectureContext
+public class ArchitectureContext : IArchitectureContext
{
- private readonly IIocContainer _container = container ?? throw new ArgumentNullException(nameof(container));
+ private readonly IIocContainer _container;
+ private readonly Lazy _cqrsRuntime;
private readonly ConcurrentDictionary _serviceCache = new();
- private ICqrsRuntime? _cqrsRuntime;
+
+ ///
+ /// 初始化新的架构上下文,并绑定其依赖容器。
+ ///
+ ///
+ /// 当前架构使用的 IOC 容器。
+ /// CQRS runtime 与其他框架服务会通过该容器延迟解析,以避免在上下文构造阶段强制拉起整条运行时链路。
+ ///
+ /// 为 。
+ public ArchitectureContext(IIocContainer container)
+ {
+ _container = container ?? throw new ArgumentNullException(nameof(container));
+ _cqrsRuntime = new Lazy(
+ ResolveCqrsRuntime,
+ LazyThreadSafetyMode.ExecutionAndPublication);
+ }
#region CQRS Integration
///
- /// 获取 CQRS runtime seam(延迟初始化)。
+ /// 获取 CQRS runtime seam。
///
- private ICqrsRuntime CqrsRuntime => _cqrsRuntime ??=
- _container.Get() ?? throw new InvalidOperationException("ICqrsRuntime not registered");
+ ///
+ /// 该实例会在首次访问时从容器解析,并通过 保证并发场景下只执行一次初始化,
+ /// 避免多个请求线程重复触发同一个 runtime 的容器解析。
+ ///
+ private ICqrsRuntime CqrsRuntime => _cqrsRuntime.Value;
+
+ ///
+ /// 从容器解析当前架构上下文依赖的 CQRS runtime。
+ ///
+ /// 已注册的 CQRS runtime 实例。
+ /// 容器中未注册 。
+ private ICqrsRuntime ResolveCqrsRuntime()
+ {
+ return _container.Get() ?? throw new InvalidOperationException("ICqrsRuntime not registered");
+ }
///
/// 获取指定类型的服务实例,如果缓存中存在则直接返回,否则从容器中获取并缓存
diff --git a/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj b/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
index 09cded18..6c05a59d 100644
--- a/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
+++ b/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
@@ -17,6 +17,7 @@
+
diff --git a/GFramework.Cqrs.Tests/GlobalUsings.cs b/GFramework.Cqrs.Tests/GlobalUsings.cs
index d31630ed..9a04018e 100644
--- a/GFramework.Cqrs.Tests/GlobalUsings.cs
+++ b/GFramework.Cqrs.Tests/GlobalUsings.cs
@@ -20,6 +20,7 @@ global using System.Reflection;
global using System.Runtime.CompilerServices;
global using System.Threading;
global using System.Threading.Tasks;
+global using GFramework.Tests.Shared;
global using Microsoft.Extensions.DependencyInjection;
global using Moq;
global using NUnit.Compatibility;
diff --git a/GFramework.Cqrs.Tests/CqrsTestRuntime.cs b/tests/Shared/CqrsTestRuntime.cs
similarity index 76%
rename from GFramework.Cqrs.Tests/CqrsTestRuntime.cs
rename to tests/Shared/CqrsTestRuntime.cs
index 7676f143..a234ff90 100644
--- a/GFramework.Cqrs.Tests/CqrsTestRuntime.cs
+++ b/tests/Shared/CqrsTestRuntime.cs
@@ -6,23 +6,21 @@ using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Cqrs.Tests;
+namespace GFramework.Tests.Shared;
///
/// 为测试项目提供对 CQRS 处理器真实注册入口的受控访问。
///
///
-/// 测试应通过该入口驱动注册流程,而不是直接反射调用注册器的私有辅助方法,
-/// 这样可以覆盖生产启动路径中的程序集去重、日志记录与容错恢复行为。
+/// 该文件以共享源码的方式同时编译进多个测试项目,确保反射绑定签名、默认 runtime 接线和注册入口行为始终保持一致,
+/// 避免测试副本在独立演化后产生隐藏分歧。
///
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.");
+ .GetType(
+ "GFramework.Core.Cqrs.Internal.CqrsHandlerRegistrar",
+ throwOnError: true)!;
private static readonly MethodInfo RegisterHandlersMethod = CqrsHandlerRegistrarType
.GetMethod(
@@ -40,11 +38,9 @@ internal static class CqrsTestRuntime
"Failed to locate CqrsHandlerRegistrar.RegisterHandlers.");
private static readonly Type CqrsDispatcherType = typeof(ArchitectureContext).Assembly
- .GetType(
- "GFramework.Core.Cqrs.Internal.CqrsDispatcher",
- throwOnError: true)!
- ?? throw new InvalidOperationException(
- "Failed to locate CqrsDispatcher type.");
+ .GetType(
+ "GFramework.Core.Cqrs.Internal.CqrsDispatcher",
+ throwOnError: true)!;
private static readonly ConstructorInfo CqrsDispatcherConstructor = CqrsDispatcherType.GetConstructor(
BindingFlags.Instance |
@@ -60,11 +56,9 @@ internal static class CqrsTestRuntime
"Failed to locate CqrsDispatcher constructor.");
private static readonly Type DefaultCqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
- .GetType(
- "GFramework.Core.Cqrs.Internal.DefaultCqrsHandlerRegistrar",
- throwOnError: true)!
- ?? throw new InvalidOperationException(
- "Failed to locate DefaultCqrsHandlerRegistrar type.");
+ .GetType(
+ "GFramework.Core.Cqrs.Internal.DefaultCqrsHandlerRegistrar",
+ throwOnError: true)!;
private static readonly ConstructorInfo DefaultCqrsHandlerRegistrarConstructor =
DefaultCqrsHandlerRegistrarType.GetConstructor(
From f9cc1237a3c8e2ed0db03dcb63fe0e4532ef955c Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 16:22:09 +0800
Subject: [PATCH 10/16] =?UTF-8?q?chore(project):=20=E5=88=9D=E5=A7=8B?=
=?UTF-8?q?=E5=8C=96=E9=A1=B9=E7=9B=AE=E7=BB=93=E6=9E=84=E5=92=8C=E6=B5=8B?=
=?UTF-8?q?=E8=AF=95=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 创建 GFramework.Core.Tests 和 GFramework.Cqrs.Tests 测试项目
- 配置测试项目的全局 using 语句和依赖引用
- 添加主项目 GFramework 的元包配置文件
- 生成解决方案文件并配置所有项目引用关系
- 设置多目标框架支持 net8.0、net9.0 和 net10.0
- 配置包发布设置和仓库信息
---
.../GFramework.Core.Tests.csproj | 2 +-
GFramework.Core.Tests/GlobalUsings.cs | 2 +-
.../GFramework.Cqrs.Tests.csproj | 2 +-
GFramework.Cqrs.Tests/GlobalUsings.cs | 2 +-
.../CqrsTestRuntime.cs | 22 ++++++++++++-------
.../GFramework.Tests.Common.csproj | 17 ++++++++++++++
GFramework.csproj | 3 +++
GFramework.sln | 14 ++++++++++++
8 files changed, 52 insertions(+), 12 deletions(-)
rename {tests/Shared => GFramework.Tests.Common}/CqrsTestRuntime.cs (90%)
create mode 100644 GFramework.Tests.Common/GFramework.Tests.Common.csproj
diff --git a/GFramework.Core.Tests/GFramework.Core.Tests.csproj b/GFramework.Core.Tests/GFramework.Core.Tests.csproj
index c455f570..391b2924 100644
--- a/GFramework.Core.Tests/GFramework.Core.Tests.csproj
+++ b/GFramework.Core.Tests/GFramework.Core.Tests.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/GFramework.Core.Tests/GlobalUsings.cs b/GFramework.Core.Tests/GlobalUsings.cs
index daaaf5b7..96b8bb1a 100644
--- a/GFramework.Core.Tests/GlobalUsings.cs
+++ b/GFramework.Core.Tests/GlobalUsings.cs
@@ -16,7 +16,7 @@ global using System.Collections.Generic;
global using System.Linq;
global using System.Threading;
global using System.Threading.Tasks;
-global using GFramework.Tests.Shared;
+global using GFramework.Tests.Common;
global using NUnit.Framework;
global using NUnit.Compatibility;
global using GFramework.Core.Systems;
diff --git a/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj b/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
index 6c05a59d..796883e4 100644
--- a/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
+++ b/GFramework.Cqrs.Tests/GFramework.Cqrs.Tests.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/GFramework.Cqrs.Tests/GlobalUsings.cs b/GFramework.Cqrs.Tests/GlobalUsings.cs
index 9a04018e..c47473b0 100644
--- a/GFramework.Cqrs.Tests/GlobalUsings.cs
+++ b/GFramework.Cqrs.Tests/GlobalUsings.cs
@@ -20,7 +20,7 @@ global using System.Reflection;
global using System.Runtime.CompilerServices;
global using System.Threading;
global using System.Threading.Tasks;
-global using GFramework.Tests.Shared;
+global using GFramework.Tests.Common;
global using Microsoft.Extensions.DependencyInjection;
global using Moq;
global using NUnit.Compatibility;
diff --git a/tests/Shared/CqrsTestRuntime.cs b/GFramework.Tests.Common/CqrsTestRuntime.cs
similarity index 90%
rename from tests/Shared/CqrsTestRuntime.cs
rename to GFramework.Tests.Common/CqrsTestRuntime.cs
index a234ff90..50890658 100644
--- a/tests/Shared/CqrsTestRuntime.cs
+++ b/GFramework.Tests.Common/CqrsTestRuntime.cs
@@ -1,3 +1,7 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
@@ -6,16 +10,16 @@ using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Cqrs.Abstractions.Cqrs;
-namespace GFramework.Tests.Shared;
+namespace GFramework.Tests.Common;
///
/// 为测试项目提供对 CQRS 处理器真实注册入口的受控访问。
///
///
-/// 该文件以共享源码的方式同时编译进多个测试项目,确保反射绑定签名、默认 runtime 接线和注册入口行为始终保持一致,
-/// 避免测试副本在独立演化后产生隐藏分歧。
+/// 该测试基础设施位于独立模块中,避免多个测试项目复制同一份反射绑定与默认 runtime 接线逻辑。
+/// 测试应通过该入口驱动注册流程,而不是各自维护一份实现细节副本。
///
-internal static class CqrsTestRuntime
+public static class CqrsTestRuntime
{
private static readonly Type CqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly
.GetType(
@@ -74,11 +78,13 @@ internal static class CqrsTestRuntime
///
/// 为裸测试容器补齐默认 CQRS runtime seam。
- /// 这使仅使用 的测试环境也能观察与生产路径一致的 runtime 行为,
- /// 而无需完整启动服务模块管理器。
///
/// 目标测试容器。
- internal static void RegisterInfrastructure(MicrosoftDiContainer container)
+ ///
+ /// 这使仅使用 的测试环境也能观察与生产路径一致的 runtime 行为,
+ /// 而无需完整启动服务模块管理器。
+ ///
+ public static void RegisterInfrastructure(MicrosoftDiContainer container)
{
ArgumentNullException.ThrowIfNull(container);
@@ -97,7 +103,7 @@ internal static class CqrsTestRuntime
///
/// 承载处理器映射的测试容器。
/// 要扫描的程序集集合。
- internal static void RegisterHandlers(MicrosoftDiContainer container, params Assembly[] assemblies)
+ public static void RegisterHandlers(MicrosoftDiContainer container, params Assembly[] assemblies)
{
ArgumentNullException.ThrowIfNull(container);
ArgumentNullException.ThrowIfNull(assemblies);
diff --git a/GFramework.Tests.Common/GFramework.Tests.Common.csproj b/GFramework.Tests.Common/GFramework.Tests.Common.csproj
new file mode 100644
index 00000000..51f8fba9
--- /dev/null
+++ b/GFramework.Tests.Common/GFramework.Tests.Common.csproj
@@ -0,0 +1,17 @@
+
+
+
+ net10.0
+ disable
+ enable
+ true
+ false
+
+
+
+
+
+
+
+
+
diff --git a/GFramework.csproj b/GFramework.csproj
index 679cbf64..b9c6c7a4 100644
--- a/GFramework.csproj
+++ b/GFramework.csproj
@@ -66,6 +66,7 @@
+
@@ -110,6 +111,7 @@
+
@@ -140,6 +142,7 @@
+
diff --git a/GFramework.sln b/GFramework.sln
index 088d63d3..bc64ce21 100644
--- a/GFramework.sln
+++ b/GFramework.sln
@@ -44,6 +44,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs", "GFramewo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Cqrs.Tests", "GFramework.Cqrs.Tests\GFramework.Cqrs.Tests.csproj", "{29037A55-9A89-425C-AB33-D44872B2E601}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GFramework.Tests.Common", "GFramework.Tests.Common\GFramework.Tests.Common.csproj", "{1100EE3E-A12D-4DE5-ABA8-591D3126570B}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -318,6 +320,18 @@ Global
{29037A55-9A89-425C-AB33-D44872B2E601}.Release|x64.Build.0 = Release|Any CPU
{29037A55-9A89-425C-AB33-D44872B2E601}.Release|x86.ActiveCfg = Release|Any CPU
{29037A55-9A89-425C-AB33-D44872B2E601}.Release|x86.Build.0 = Release|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Debug|x64.Build.0 = Debug|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Debug|x86.Build.0 = Debug|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|x64.ActiveCfg = Release|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|x64.Build.0 = Release|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|x86.ActiveCfg = Release|Any CPU
+ {1100EE3E-A12D-4DE5-ABA8-591D3126570B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
From 81897ce2aca83b5ed2b6684283118bc589415911 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 17:00:49 +0800
Subject: [PATCH 11/16] =?UTF-8?q?docs(source-generators):=20=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=E6=BA=90=E7=A0=81=E7=94=9F=E6=88=90=E5=99=A8=E6=96=87?=
=?UTF-8?q?=E6=A1=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 新增 AutoRegisterExportedCollections 生成器文档
- 新增 AutoScene 生成器文档
- 新增 AutoUiPage 生成器文档
- 新增完整的源码生成器索引文档
- 详细介绍各生成器的使用方法和参数说明
- 提供生成代码示例和诊断信息说明
- 包含性能优势和使用示例章节
---
.../ArchitectureServicesTests.cs | 30 ++++++++++++
.../MediatorCompatibilityDeprecationTests.cs | 8 ++--
.../Extensions/MediatorCoroutineExtensions.cs | 2 +-
.../Cqrs/Internal/CqrsDispatcher.cs | 6 +++
.../ContextAwareMediatorCommandExtensions.cs | 2 +-
.../ContextAwareMediatorExtensions.cs | 2 +-
.../ContextAwareMediatorQueryExtensions.cs | 2 +-
.../Behavior/AutoSceneGeneratorTests.cs | 20 ++++----
.../Behavior/AutoUiPageGeneratorTests.cs | 12 ++---
...gisterExportedCollectionsGeneratorTests.cs | 48 +++++++++----------
.../Behavior/AutoSceneGenerator.cs | 3 +-
.../Behavior/AutoUiPageGenerator.cs | 2 +-
...utoRegisterExportedCollectionsGenerator.cs | 4 +-
...register-exported-collections-generator.md | 3 +-
.../source-generators/auto-scene-generator.md | 3 +-
.../auto-ui-page-generator.md | 5 +-
docs/zh-CN/source-generators/index.md | 6 +--
17 files changed, 99 insertions(+), 59 deletions(-)
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
index 57dc19a5..52c2ecbd 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureServicesTests.cs
@@ -359,6 +359,14 @@ public class TestArchitectureContextV3 : IArchitectureContext
throw new NotImplementedException();
}
+ ///
+ /// 测试桩:异步发送 CQRS 命令并返回响应。
+ ///
+ /// 命令响应类型。
+ /// 要发送的命令。
+ /// 取消令牌。
+ /// 命令响应任务。
+ /// 该测试桩未实现此成员。
public ValueTask SendCommandAsync(
GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command,
CancellationToken cancellationToken = default)
@@ -366,11 +374,26 @@ public class TestArchitectureContextV3 : IArchitectureContext
throw new NotImplementedException();
}
+ ///
+ /// 测试桩:同步发送 CQRS 命令并返回响应。
+ ///
+ /// 命令响应类型。
+ /// 要发送的命令。
+ /// 命令响应。
+ /// 该测试桩未实现此成员。
public TResponse SendCommand(GFramework.Cqrs.Abstractions.Cqrs.Command.ICommand command)
{
throw new NotImplementedException();
}
+ ///
+ /// 测试桩:异步发送 CQRS 查询并返回结果。
+ ///
+ /// 查询结果类型。
+ /// 要发送的查询。
+ /// 取消令牌。
+ /// 查询结果任务。
+ /// 该测试桩未实现此成员。
public ValueTask SendQueryAsync(
GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query,
CancellationToken cancellationToken = default)
@@ -378,6 +401,13 @@ public class TestArchitectureContextV3 : IArchitectureContext
throw new NotImplementedException();
}
+ ///
+ /// 测试桩:同步发送 CQRS 查询并返回结果。
+ ///
+ /// 查询结果类型。
+ /// 要发送的查询。
+ /// 查询结果。
+ /// 该测试桩未实现此成员。
public TResponse SendQuery(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery query)
{
throw new NotImplementedException();
diff --git a/GFramework.Core.Tests/Cqrs/MediatorCompatibilityDeprecationTests.cs b/GFramework.Core.Tests/Cqrs/MediatorCompatibilityDeprecationTests.cs
index 2e3bdbca..b25806f0 100644
--- a/GFramework.Core.Tests/Cqrs/MediatorCompatibilityDeprecationTests.cs
+++ b/GFramework.Core.Tests/Cqrs/MediatorCompatibilityDeprecationTests.cs
@@ -35,16 +35,16 @@ public class MediatorCompatibilityDeprecationTests
{
AssertLegacyType(
typeof(ContextAwareMediatorExtensions),
- "Use GFramework.Core.Cqrs.Extensions.ContextAwareCqrsExtensions instead.");
+ "Use GFramework.Core.Extensions.ContextAwareCqrsExtensions instead.");
AssertLegacyType(
typeof(ContextAwareMediatorCommandExtensions),
- "Use GFramework.Core.Cqrs.Extensions.ContextAwareCqrsCommandExtensions instead.");
+ "Use GFramework.Core.Extensions.ContextAwareCqrsCommandExtensions instead.");
AssertLegacyType(
typeof(ContextAwareMediatorQueryExtensions),
- "Use GFramework.Core.Cqrs.Extensions.ContextAwareCqrsQueryExtensions instead.");
+ "Use GFramework.Core.Extensions.ContextAwareCqrsQueryExtensions instead.");
AssertLegacyType(
typeof(MediatorCoroutineExtensions),
- "Use GFramework.Core.Cqrs.Extensions.CqrsCoroutineExtensions instead.");
+ "Use GFramework.Core.Coroutine.Extensions.CqrsCoroutineExtensions instead.");
}
///
diff --git a/GFramework.Core/Coroutine/Extensions/MediatorCoroutineExtensions.cs b/GFramework.Core/Coroutine/Extensions/MediatorCoroutineExtensions.cs
index 70e3c29f..5086e392 100644
--- a/GFramework.Core/Coroutine/Extensions/MediatorCoroutineExtensions.cs
+++ b/GFramework.Core/Coroutine/Extensions/MediatorCoroutineExtensions.cs
@@ -25,7 +25,7 @@ namespace GFramework.Core.Coroutine.Extensions;
///
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
- "Use GFramework.Core.Cqrs.Extensions.CqrsCoroutineExtensions instead. This compatibility alias will be removed in a future major version.")]
+ "Use GFramework.Core.Coroutine.Extensions.CqrsCoroutineExtensions instead. This compatibility alias will be removed in a future major version.")]
public static class MediatorCoroutineExtensions
{
///
diff --git a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs b/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
index 4e2c739d..0cae34c4 100644
--- a/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
+++ b/GFramework.Core/Cqrs/Internal/CqrsDispatcher.cs
@@ -17,14 +17,20 @@ internal sealed class CqrsDispatcher(
IIocContainer container,
ILogger logger) : ICqrsRuntime
{
+ // 进程级缓存:按请求/响应类型缓存直接处理器调用委托,避免热路径重复反射。
+ // 线程安全依赖 ConcurrentDictionary;缓存与进程同寿命,默认假设请求类型集合有限且稳定。
private static readonly ConcurrentDictionary<(Type RequestType, Type ResponseType), RequestInvoker>
RequestInvokers = new();
+ // 进程级缓存:缓存带 pipeline 的请求调用委托,减少每次分发时的反射与表达式重建开销。
+ // 若后续引入动态生成请求类型,需要重新评估该缓存的增长边界。
private static readonly ConcurrentDictionary<(Type RequestType, Type ResponseType), RequestPipelineInvoker>
RequestPipelineInvokers = new();
+ // 进程级缓存:缓存通知调用委托,复用并发安全字典以支撑多线程发布路径。
private static readonly ConcurrentDictionary NotificationInvokers = new();
+ // 进程级缓存:缓存流式请求调用委托,避免每次创建流时重复解析反射签名。
private static readonly ConcurrentDictionary<(Type RequestType, Type ResponseType), StreamInvoker> StreamInvokers =
new();
diff --git a/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
index 41d9df27..4ab0692b 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorCommandExtensions.cs
@@ -11,7 +11,7 @@ namespace GFramework.Core.Extensions;
///
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
- "Use GFramework.Core.Cqrs.Extensions.ContextAwareCqrsCommandExtensions instead. This compatibility alias will be removed in a future major version.")]
+ "Use GFramework.Core.Extensions.ContextAwareCqrsCommandExtensions instead. This compatibility alias will be removed in a future major version.")]
public static class ContextAwareMediatorCommandExtensions
{
///
diff --git a/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs
index b4d607bc..f2294930 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorExtensions.cs
@@ -11,7 +11,7 @@ namespace GFramework.Core.Extensions;
///
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
- "Use GFramework.Core.Cqrs.Extensions.ContextAwareCqrsExtensions instead. This compatibility alias will be removed in a future major version.")]
+ "Use GFramework.Core.Extensions.ContextAwareCqrsExtensions instead. This compatibility alias will be removed in a future major version.")]
public static class ContextAwareMediatorExtensions
{
///
diff --git a/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs b/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
index 49b57445..61b02cb9 100644
--- a/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
+++ b/GFramework.Core/Extensions/ContextAwareMediatorQueryExtensions.cs
@@ -11,7 +11,7 @@ namespace GFramework.Core.Extensions;
///
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete(
- "Use GFramework.Core.Cqrs.Extensions.ContextAwareCqrsQueryExtensions instead. This compatibility alias will be removed in a future major version.")]
+ "Use GFramework.Core.Extensions.ContextAwareCqrsQueryExtensions instead. This compatibility alias will be removed in a future major version.")]
public static class ContextAwareMediatorQueryExtensions
{
///
diff --git a/GFramework.Godot.SourceGenerators.Tests/Behavior/AutoSceneGeneratorTests.cs b/GFramework.Godot.SourceGenerators.Tests/Behavior/AutoSceneGeneratorTests.cs
index b50b0d73..2c6dc972 100644
--- a/GFramework.Godot.SourceGenerators.Tests/Behavior/AutoSceneGeneratorTests.cs
+++ b/GFramework.Godot.SourceGenerators.Tests/Behavior/AutoSceneGeneratorTests.cs
@@ -11,10 +11,10 @@ public class AutoSceneGeneratorTests
{
const string source = """
using System;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
using Godot;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoSceneAttribute : Attribute
@@ -88,10 +88,10 @@ public class AutoSceneGeneratorTests
{
const string source = """
using System;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
using Godot;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoSceneAttribute : Attribute
@@ -137,10 +137,10 @@ public class AutoSceneGeneratorTests
const string source = """
#nullable enable
using System;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
using Godot;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoSceneAttribute : Attribute
@@ -225,10 +225,10 @@ public class AutoSceneGeneratorTests
{
const string source = """
using System;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
using Godot;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoSceneAttribute : Attribute
@@ -279,10 +279,10 @@ public class AutoSceneGeneratorTests
const string source = """
using System;
using GFramework.Game.Abstractions.Scene;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
using Godot;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoSceneAttribute : Attribute
diff --git a/GFramework.Godot.SourceGenerators.Tests/Behavior/AutoUiPageGeneratorTests.cs b/GFramework.Godot.SourceGenerators.Tests/Behavior/AutoUiPageGeneratorTests.cs
index 8e7d1115..2b8aca67 100644
--- a/GFramework.Godot.SourceGenerators.Tests/Behavior/AutoUiPageGeneratorTests.cs
+++ b/GFramework.Godot.SourceGenerators.Tests/Behavior/AutoUiPageGeneratorTests.cs
@@ -11,10 +11,10 @@ public class AutoUiPageGeneratorTests
{
const string source = """
using System;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
using Godot;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoUiPageAttribute : Attribute
@@ -100,10 +100,10 @@ public class AutoUiPageGeneratorTests
{
const string source = """
using System;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
using Godot;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoUiPageAttribute : Attribute
@@ -183,10 +183,10 @@ public class AutoUiPageGeneratorTests
const string source = """
#nullable enable
using System;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
using Godot;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoUiPageAttribute : Attribute
diff --git a/GFramework.Godot.SourceGenerators.Tests/Registration/AutoRegisterExportedCollectionsGeneratorTests.cs b/GFramework.Godot.SourceGenerators.Tests/Registration/AutoRegisterExportedCollectionsGeneratorTests.cs
index 3a67d3c5..befdcf59 100644
--- a/GFramework.Godot.SourceGenerators.Tests/Registration/AutoRegisterExportedCollectionsGeneratorTests.cs
+++ b/GFramework.Godot.SourceGenerators.Tests/Registration/AutoRegisterExportedCollectionsGeneratorTests.cs
@@ -13,9 +13,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
#nullable enable
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -86,9 +86,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
const string source = """
using System;
using System.Collections;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -141,9 +141,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
#nullable enable
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -207,9 +207,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
#nullable enable
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -284,9 +284,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
const string source = """
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -344,9 +344,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
#nullable enable
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -414,9 +414,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
#nullable enable
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -482,9 +482,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
const string source = """
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -549,9 +549,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
const string source = """
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -604,9 +604,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
const string source = """
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -659,9 +659,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
const string source = """
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
@@ -715,9 +715,9 @@ public class AutoRegisterExportedCollectionsGeneratorTests
#nullable enable
using System;
using System.Collections.Generic;
- using GFramework.Godot.SourceGenerators.Abstractions;
+ using GFramework.Godot.SourceGenerators.Abstractions.UI;
- namespace GFramework.Godot.SourceGenerators.Abstractions
+ namespace GFramework.Godot.SourceGenerators.Abstractions.UI
{
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
public sealed class AutoRegisterExportedCollectionsAttribute : Attribute { }
diff --git a/GFramework.Godot.SourceGenerators/Behavior/AutoSceneGenerator.cs b/GFramework.Godot.SourceGenerators/Behavior/AutoSceneGenerator.cs
index f2920be4..ee389f0e 100644
--- a/GFramework.Godot.SourceGenerators/Behavior/AutoSceneGenerator.cs
+++ b/GFramework.Godot.SourceGenerators/Behavior/AutoSceneGenerator.cs
@@ -18,7 +18,8 @@ namespace GFramework.Godot.SourceGenerators.Behavior;
public sealed class AutoSceneGenerator : IIncrementalGenerator
{
private const string AutoSceneAttributeMetadataName =
- $"{PathContests.GodotSourceGeneratorsAbstractionsPath}.AutoSceneAttribute";
+ $"{PathContests.GodotSourceGeneratorsAbstractionsPath}.UI.AutoSceneAttribute";
+
private static readonly string[] GeneratedMemberNames =
[
"SceneKeyStr",
diff --git a/GFramework.Godot.SourceGenerators/Behavior/AutoUiPageGenerator.cs b/GFramework.Godot.SourceGenerators/Behavior/AutoUiPageGenerator.cs
index 1e0fe7f6..ca193956 100644
--- a/GFramework.Godot.SourceGenerators/Behavior/AutoUiPageGenerator.cs
+++ b/GFramework.Godot.SourceGenerators/Behavior/AutoUiPageGenerator.cs
@@ -12,7 +12,7 @@ namespace GFramework.Godot.SourceGenerators.Behavior;
public sealed class AutoUiPageGenerator : IIncrementalGenerator
{
private const string AutoUiPageAttributeMetadataName =
- $"{PathContests.GodotSourceGeneratorsAbstractionsPath}.AutoUiPageAttribute";
+ $"{PathContests.GodotSourceGeneratorsAbstractionsPath}.UI.AutoUiPageAttribute";
public void Initialize(IncrementalGeneratorInitializationContext context)
{
diff --git a/GFramework.Godot.SourceGenerators/Registration/AutoRegisterExportedCollectionsGenerator.cs b/GFramework.Godot.SourceGenerators/Registration/AutoRegisterExportedCollectionsGenerator.cs
index 12589767..93219dcb 100644
--- a/GFramework.Godot.SourceGenerators/Registration/AutoRegisterExportedCollectionsGenerator.cs
+++ b/GFramework.Godot.SourceGenerators/Registration/AutoRegisterExportedCollectionsGenerator.cs
@@ -18,10 +18,10 @@ namespace GFramework.Godot.SourceGenerators.Registration;
public sealed class AutoRegisterExportedCollectionsGenerator : IIncrementalGenerator
{
private const string AutoRegisterExportedCollectionsAttributeMetadataName =
- $"{PathContests.GodotSourceGeneratorsAbstractionsPath}.AutoRegisterExportedCollectionsAttribute";
+ $"{PathContests.GodotSourceGeneratorsAbstractionsPath}.UI.AutoRegisterExportedCollectionsAttribute";
private const string RegisterExportedCollectionAttributeMetadataName =
- $"{PathContests.GodotSourceGeneratorsAbstractionsPath}.RegisterExportedCollectionAttribute";
+ $"{PathContests.GodotSourceGeneratorsAbstractionsPath}.UI.RegisterExportedCollectionAttribute";
private const string GeneratedMethodName = "__RegisterExportedCollections_Generated";
diff --git a/docs/zh-CN/source-generators/auto-register-exported-collections-generator.md b/docs/zh-CN/source-generators/auto-register-exported-collections-generator.md
index 2d73f531..8d1ca366 100644
--- a/docs/zh-CN/source-generators/auto-register-exported-collections-generator.md
+++ b/docs/zh-CN/source-generators/auto-register-exported-collections-generator.md
@@ -13,12 +13,13 @@
`AutoRegisterExportedCollections` 会把这类样板收敛成声明式配置。
它特别适合 `GameEntryPoint`、资源根节点、配置引导节点这类“导出即注册”的场景。
+相关特性当前位于 `GFramework.Godot.SourceGenerators.Abstractions.UI` 命名空间。
## 基础使用
```csharp
using System.Collections.Generic;
-using GFramework.Godot.SourceGenerators.Abstractions;
+using GFramework.Godot.SourceGenerators.Abstractions.UI;
using Godot;
public interface IKeyValue
diff --git a/docs/zh-CN/source-generators/auto-scene-generator.md b/docs/zh-CN/source-generators/auto-scene-generator.md
index 1a2ff881..92927daa 100644
--- a/docs/zh-CN/source-generators/auto-scene-generator.md
+++ b/docs/zh-CN/source-generators/auto-scene-generator.md
@@ -12,11 +12,12 @@
- `GetScene()` 包装方法
`AutoScene` 会在编译期生成这些固定样板。
+该特性当前位于 `GFramework.Godot.SourceGenerators.Abstractions.UI` 命名空间。
## 基础使用
```csharp
-using GFramework.Godot.SourceGenerators.Abstractions;
+using GFramework.Godot.SourceGenerators.Abstractions.UI;
using GFramework.Game.Abstractions.Enums;
using Godot;
diff --git a/docs/zh-CN/source-generators/auto-ui-page-generator.md b/docs/zh-CN/source-generators/auto-ui-page-generator.md
index b455eab8..1c1ecd48 100644
--- a/docs/zh-CN/source-generators/auto-ui-page-generator.md
+++ b/docs/zh-CN/source-generators/auto-ui-page-generator.md
@@ -13,11 +13,12 @@
- `GetPage()` 工厂包装
`AutoUiPage` 会把这部分样板迁移到编译期生成。
+该特性当前位于 `GFramework.Godot.SourceGenerators.Abstractions.UI` 命名空间。
## 基础使用
```csharp
-using GFramework.Godot.SourceGenerators.Abstractions;
+using GFramework.Godot.SourceGenerators.Abstractions.UI;
using GFramework.Game.Abstractions.Enums;
using Godot;
@@ -123,7 +124,7 @@ partial class MainMenu
## 组合示例
```csharp
-using GFramework.Godot.SourceGenerators.Abstractions;
+using GFramework.Godot.SourceGenerators.Abstractions.UI;
using GFramework.Game.Abstractions.Enums;
using GFramework.SourceGenerators.Abstractions.Rule;
using Godot;
diff --git a/docs/zh-CN/source-generators/index.md b/docs/zh-CN/source-generators/index.md
index 8fd91e66..ea62aad0 100644
--- a/docs/zh-CN/source-generators/index.md
+++ b/docs/zh-CN/source-generators/index.md
@@ -610,7 +610,7 @@ AutoUiPage 生成器为 Godot 页面节点自动生成 `UiKeyStr`、缓存的 `I
### 基础示例
```csharp
-using GFramework.Godot.SourceGenerators.Abstractions;
+using GFramework.Godot.SourceGenerators.Abstractions.UI;
using GFramework.Game.Abstractions.Enums;
using Godot;
@@ -635,7 +635,7 @@ AutoScene 生成器为场景根节点自动生成 `SceneKeyStr`、缓存的 `ISc
### 基础示例
```csharp
-using GFramework.Godot.SourceGenerators.Abstractions;
+using GFramework.Godot.SourceGenerators.Abstractions.UI;
using GFramework.Game.Abstractions.Enums;
using Godot;
@@ -660,7 +660,7 @@ AutoRegisterExportedCollections 生成器为 Godot 导出集合自动生成批
### 基础示例
```csharp
-using GFramework.Godot.SourceGenerators.Abstractions;
+using GFramework.Godot.SourceGenerators.Abstractions.UI;
using Godot;
[AutoRegisterExportedCollections]
From 82e6332a9b7a8bc7b62bea2728ccc44ad9bd3d26 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 17:47:54 +0800
Subject: [PATCH 12/16] =?UTF-8?q?test(core):=20=E6=B7=BB=E5=8A=A0=E6=9E=B6?=
=?UTF-8?q?=E6=9E=84=E4=B8=8A=E4=B8=8B=E6=96=87=E5=92=8C=E4=BE=9D=E8=B5=96?=
=?UTF-8?q?=E6=B3=A8=E5=85=A5=E5=AE=B9=E5=99=A8=E7=9A=84=E5=8D=95=E5=85=83?=
=?UTF-8?q?=E6=B5=8B=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 实现 ArchitectureContext 类的全面单元测试,覆盖构造函数、查询命令事件发送等功能
- 添加 MicrosoftDiContainer 依赖注入容器的完整测试,包括注册、获取、冻结等操作
- 创建 CqrsTestRuntime 测试基础设施,提供对 CQRS 处理器注册的受控访问
- 测试并发场景下的线程安全性,验证多线程环境下容器操作的正确性
- 实现优先级排序功能测试,确保服务按优先级正确排序和注册
- 添加各种边界条件测试,包括空参数异常处理和重复注册异常检测
---
.../Architectures/ArchitectureContextTests.cs | 29 +++++++++-------
.../Ioc/MicrosoftDiContainerTests.cs | 21 ++++++++++--
GFramework.Tests.Common/CqrsTestRuntime.cs | 34 ++++++++++++++-----
3 files changed, 60 insertions(+), 24 deletions(-)
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
index 4b5da422..63d7ffae 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
@@ -16,7 +16,6 @@ using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Query;
-using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Architectures;
@@ -45,6 +44,15 @@ namespace GFramework.Core.Tests.Architectures;
[TestFixture]
public class ArchitectureContextTests
{
+ private AsyncQueryExecutor? _asyncQueryBus;
+ private CommandExecutor? _commandBus;
+ private MicrosoftDiContainer? _container;
+
+ private ArchitectureContext? _context;
+ private DefaultEnvironment? _environment;
+ private EventBus? _eventBus;
+ private QueryExecutor? _queryBus;
+
[SetUp]
public void SetUp()
{
@@ -76,14 +84,6 @@ public class ArchitectureContextTests
_context = new ArchitectureContext(_container);
}
- private ArchitectureContext? _context;
- private MicrosoftDiContainer? _container;
- private EventBus? _eventBus;
- private CommandExecutor? _commandBus;
- private QueryExecutor? _queryBus;
- private AsyncQueryExecutor? _asyncQueryBus;
- private DefaultEnvironment? _environment;
-
///
/// 测试构造函数在所有参数都有效时不应抛出异常
///
@@ -308,8 +308,10 @@ public class ArchitectureContextTests
[Test]
public async Task SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently()
{
+ const int workerCount = 16;
using var startGate = new ManualResetEventSlim(false);
using var allowResolutionToComplete = new ManualResetEventSlim(false);
+ using var workersReady = new CountdownEvent(workerCount);
var resolutionCallCount = 0;
var runtime = new Mock(MockBehavior.Strict);
var container = new Mock(MockBehavior.Strict);
@@ -329,14 +331,19 @@ public class ArchitectureContextTests
});
var context = new ArchitectureContext(container.Object);
- var requests = Enumerable.Range(0, 16)
+ var requests = Enumerable.Range(0, workerCount)
.Select(_ => Task.Run(async () =>
{
+ workersReady.Signal();
startGate.Wait();
return await context.SendRequestAsync(new TestCqrsRequest());
}))
.ToArray();
+ Assert.That(
+ workersReady.Wait(TimeSpan.FromSeconds(1)),
+ Is.True,
+ "Expected all workers to be ready before releasing start gate.");
startGate.Set();
Assert.That(
@@ -344,8 +351,6 @@ public class ArchitectureContextTests
Is.True,
"Expected at least one CQRS runtime resolution attempt.");
- // 留出一个短暂窗口,让并发首次访问都在 runtime 尚未发布前抵达同一初始化点。
- await Task.Delay(50);
allowResolutionToComplete.Set();
var responses = await Task.WhenAll(requests);
diff --git a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
index 3faedecb..275101d9 100644
--- a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
+++ b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
@@ -1,10 +1,10 @@
using System.Reflection;
using GFramework.Core.Abstractions.Bases;
+using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Tests.Cqrs;
using GFramework.Core.Tests.Systems;
-using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Ioc;
@@ -14,6 +14,8 @@ namespace GFramework.Core.Tests.Ioc;
[TestFixture]
public class MicrosoftDiContainerTests
{
+ private MicrosoftDiContainer _container = null!;
+
///
/// 在每个测试方法执行前进行设置
///
@@ -33,8 +35,6 @@ public class MicrosoftDiContainerTests
CqrsTestRuntime.RegisterInfrastructure(_container);
}
- private MicrosoftDiContainer _container = null!;
-
///
/// 测试注册单例实例的功能
///
@@ -151,6 +151,21 @@ public class MicrosoftDiContainerTests
Assert.That(result, Is.SameAs(instance));
}
+ ///
+ /// 测试当 CQRS 基础设施已手动接线后,再调用处理器注册入口不会重复注册 runtime seam。
+ ///
+ [Test]
+ public void RegisterHandlers_Should_Not_Duplicate_Cqrs_Infrastructure_When_It_Is_Already_Registered()
+ {
+ Assert.That(_container.GetAll(), Has.Count.EqualTo(1));
+ Assert.That(_container.GetAll(), Has.Count.EqualTo(1));
+
+ CqrsTestRuntime.RegisterHandlers(_container);
+
+ Assert.That(_container.GetAll(), Has.Count.EqualTo(1));
+ Assert.That(_container.GetAll(), Has.Count.EqualTo(1));
+ }
+
///
/// 测试当没有实例时获取应返回 null 的功能
///
diff --git a/GFramework.Tests.Common/CqrsTestRuntime.cs b/GFramework.Tests.Common/CqrsTestRuntime.cs
index 50890658..57d4effd 100644
--- a/GFramework.Tests.Common/CqrsTestRuntime.cs
+++ b/GFramework.Tests.Common/CqrsTestRuntime.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using System.Reflection;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
@@ -80,22 +79,31 @@ public static class CqrsTestRuntime
/// 为裸测试容器补齐默认 CQRS runtime seam。
///
/// 目标测试容器。
+ /// 为 。
+ /// 反射调用底层 CQRS runtime 或注册器构造函数失败时抛出。
///
/// 这使仅使用 的测试环境也能观察与生产路径一致的 runtime 行为,
/// 而无需完整启动服务模块管理器。
+ /// 该方法按服务类型执行幂等注册,只会补齐当前容器中尚未接线的 CQRS 基础设施。
///
public static void RegisterInfrastructure(MicrosoftDiContainer container)
{
ArgumentNullException.ThrowIfNull(container);
- var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
- var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
- var runtime = (ICqrsRuntime)CqrsDispatcherConstructor.Invoke([container, runtimeLogger]);
- var registrar =
- (ICqrsHandlerRegistrar)DefaultCqrsHandlerRegistrarConstructor.Invoke([container, registrarLogger]);
+ if (container.Get() is null)
+ {
+ var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger(CqrsDispatcherType.Name);
+ var runtime = (ICqrsRuntime)CqrsDispatcherConstructor.Invoke([container, runtimeLogger]);
+ container.Register(runtime);
+ }
- container.Register(runtime);
- container.Register(registrar);
+ if (container.Get() is null)
+ {
+ var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(DefaultCqrsHandlerRegistrarType.Name);
+ var registrar =
+ (ICqrsHandlerRegistrar)DefaultCqrsHandlerRegistrarConstructor.Invoke([container, registrarLogger]);
+ container.Register(registrar);
+ }
}
///
@@ -103,6 +111,14 @@ public static class CqrsTestRuntime
///
/// 承载处理器映射的测试容器。
/// 要扫描的程序集集合。
+ ///
+ /// 或 为 。
+ ///
+ /// 反射调用底层 CQRS 处理器注册入口失败时抛出。
+ ///
+ /// 该入口会自动调用 ,因此测试通常无需预先手动接线 CQRS 基础设施。
+ /// 程序集去重与空元素过滤由生产注册入口统一处理,避免测试辅助层复制相同筛选逻辑。
+ ///
public static void RegisterHandlers(MicrosoftDiContainer container, params Assembly[] assemblies)
{
ArgumentNullException.ThrowIfNull(container);
@@ -113,6 +129,6 @@ public static class CqrsTestRuntime
var logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsTestRuntime));
RegisterHandlersMethod.Invoke(
null,
- [container, assemblies.Where(static assembly => assembly is not null).Distinct().ToArray(), logger]);
+ [container, assemblies, logger]);
}
}
From 2425d280979258bdd5329819c4af7e94b0599bff Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 17:49:52 +0800
Subject: [PATCH 13/16] =?UTF-8?q?refactor(tests):=20=E6=9B=B4=E6=96=B0?=
=?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=96=87=E4=BB=B6=E4=BB=A5=E6=94=AF=E6=8C=81?=
=?UTF-8?q?Cqrs=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 在ArchitectureContextTests.cs中添加GFramework.Cqrs.Abstractions.Cqrs命名空间引用
- 在MicrosoftDiContainerTests.cs中添加GFramework.Cqrs.Abstractions.Cqrs命名空间引用
- 为测试文件提供必要的Cqrs相关依赖注入支持
---
.../Architectures/ArchitectureContextTests.cs | 19 ++++++++++---------
.../Ioc/MicrosoftDiContainerTests.cs | 5 +++--
2 files changed, 13 insertions(+), 11 deletions(-)
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
index 63d7ffae..dd524414 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
@@ -16,6 +16,7 @@ using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Query;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Architectures;
@@ -44,15 +45,6 @@ namespace GFramework.Core.Tests.Architectures;
[TestFixture]
public class ArchitectureContextTests
{
- private AsyncQueryExecutor? _asyncQueryBus;
- private CommandExecutor? _commandBus;
- private MicrosoftDiContainer? _container;
-
- private ArchitectureContext? _context;
- private DefaultEnvironment? _environment;
- private EventBus? _eventBus;
- private QueryExecutor? _queryBus;
-
[SetUp]
public void SetUp()
{
@@ -84,6 +76,15 @@ public class ArchitectureContextTests
_context = new ArchitectureContext(_container);
}
+ private AsyncQueryExecutor? _asyncQueryBus;
+ private CommandExecutor? _commandBus;
+ private MicrosoftDiContainer? _container;
+
+ private ArchitectureContext? _context;
+ private DefaultEnvironment? _environment;
+ private EventBus? _eventBus;
+ private QueryExecutor? _queryBus;
+
///
/// 测试构造函数在所有参数都有效时不应抛出异常
///
diff --git a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
index 275101d9..0e03059e 100644
--- a/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
+++ b/GFramework.Core.Tests/Ioc/MicrosoftDiContainerTests.cs
@@ -5,6 +5,7 @@ using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Tests.Cqrs;
using GFramework.Core.Tests.Systems;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Ioc;
@@ -14,8 +15,6 @@ namespace GFramework.Core.Tests.Ioc;
[TestFixture]
public class MicrosoftDiContainerTests
{
- private MicrosoftDiContainer _container = null!;
-
///
/// 在每个测试方法执行前进行设置
///
@@ -35,6 +34,8 @@ public class MicrosoftDiContainerTests
CqrsTestRuntime.RegisterInfrastructure(_container);
}
+ private MicrosoftDiContainer _container = null!;
+
///
/// 测试注册单例实例的功能
///
From a068a5e707291c118a0b8bd1eb046d0bee4ed030 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 18:52:53 +0800
Subject: [PATCH 14/16] =?UTF-8?q?test(arch):=20=E4=BC=98=E5=8C=96=E6=9E=B6?=
=?UTF-8?q?=E6=9E=84=E4=B8=8A=E4=B8=8B=E6=96=87=E5=B9=B6=E5=8F=91=E6=B5=8B?=
=?UTF-8?q?=E8=AF=95=E7=9A=84=E8=B6=85=E6=97=B6=E9=85=8D=E7=BD=AE?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 将工作线程数量从16调整为8
- 添加工作线程启动超时设置为5秒
- 添加首次解析超时设置为5秒
- 使用可配置超时替代硬编码的1秒等待时间
- 提高测试稳定性和可读性
---
.../Architectures/ArchitectureContextTests.cs | 27 ++++++++++---------
1 file changed, 14 insertions(+), 13 deletions(-)
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
index dd524414..09c3f4d1 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
@@ -16,7 +16,6 @@ using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Query;
-using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Architectures;
@@ -45,6 +44,15 @@ namespace GFramework.Core.Tests.Architectures;
[TestFixture]
public class ArchitectureContextTests
{
+ private AsyncQueryExecutor? _asyncQueryBus;
+ private CommandExecutor? _commandBus;
+ private MicrosoftDiContainer? _container;
+
+ private ArchitectureContext? _context;
+ private DefaultEnvironment? _environment;
+ private EventBus? _eventBus;
+ private QueryExecutor? _queryBus;
+
[SetUp]
public void SetUp()
{
@@ -76,15 +84,6 @@ public class ArchitectureContextTests
_context = new ArchitectureContext(_container);
}
- private AsyncQueryExecutor? _asyncQueryBus;
- private CommandExecutor? _commandBus;
- private MicrosoftDiContainer? _container;
-
- private ArchitectureContext? _context;
- private DefaultEnvironment? _environment;
- private EventBus? _eventBus;
- private QueryExecutor? _queryBus;
-
///
/// 测试构造函数在所有参数都有效时不应抛出异常
///
@@ -309,7 +308,9 @@ public class ArchitectureContextTests
[Test]
public async Task SendRequestAsync_Should_ResolveCqrsRuntime_OnlyOnce_When_AccessedConcurrently()
{
- const int workerCount = 16;
+ const int workerCount = 8;
+ var workerStartupTimeout = TimeSpan.FromSeconds(5);
+ var firstResolutionTimeout = TimeSpan.FromSeconds(5);
using var startGate = new ManualResetEventSlim(false);
using var allowResolutionToComplete = new ManualResetEventSlim(false);
using var workersReady = new CountdownEvent(workerCount);
@@ -342,13 +343,13 @@ public class ArchitectureContextTests
.ToArray();
Assert.That(
- workersReady.Wait(TimeSpan.FromSeconds(1)),
+ workersReady.Wait(workerStartupTimeout),
Is.True,
"Expected all workers to be ready before releasing start gate.");
startGate.Set();
Assert.That(
- SpinWait.SpinUntil(() => Volatile.Read(ref resolutionCallCount) > 0, TimeSpan.FromSeconds(1)),
+ SpinWait.SpinUntil(() => Volatile.Read(ref resolutionCallCount) > 0, firstResolutionTimeout),
Is.True,
"Expected at least one CQRS runtime resolution attempt.");
From fe73d13991f5fcc222ddfb1c39aac43a39aac60e Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 18:55:41 +0800
Subject: [PATCH 15/16] =?UTF-8?q?chore(tests):=20=E6=B7=BB=E5=8A=A0Cqrs?=
=?UTF-8?q?=E6=8A=BD=E8=B1=A1=E4=BE=9D=E8=B5=96=E5=88=B0=E6=9E=B6=E6=9E=84?=
=?UTF-8?q?=E6=B5=8B=E8=AF=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 在ArchitectureContextTests中添加GFramework.Cqrs.Abstractions.Cqrs命名空间引用
---
.../Architectures/ArchitectureContextTests.cs | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
index 09c3f4d1..87978cd2 100644
--- a/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
+++ b/GFramework.Core.Tests/Architectures/ArchitectureContextTests.cs
@@ -16,6 +16,7 @@ using GFramework.Core.Events;
using GFramework.Core.Ioc;
using GFramework.Core.Logging;
using GFramework.Core.Query;
+using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Architectures;
@@ -44,15 +45,6 @@ namespace GFramework.Core.Tests.Architectures;
[TestFixture]
public class ArchitectureContextTests
{
- private AsyncQueryExecutor? _asyncQueryBus;
- private CommandExecutor? _commandBus;
- private MicrosoftDiContainer? _container;
-
- private ArchitectureContext? _context;
- private DefaultEnvironment? _environment;
- private EventBus? _eventBus;
- private QueryExecutor? _queryBus;
-
[SetUp]
public void SetUp()
{
@@ -84,6 +76,15 @@ public class ArchitectureContextTests
_context = new ArchitectureContext(_container);
}
+ private AsyncQueryExecutor? _asyncQueryBus;
+ private CommandExecutor? _commandBus;
+ private MicrosoftDiContainer? _container;
+
+ private ArchitectureContext? _context;
+ private DefaultEnvironment? _environment;
+ private EventBus? _eventBus;
+ private QueryExecutor? _queryBus;
+
///
/// 测试构造函数在所有参数都有效时不应抛出异常
///
From d881bd5ad1db58f344ad61bbbaa704bb99233fe6 Mon Sep 17 00:00:00 2001
From: GeWuYou <95328647+GeWuYou@users.noreply.github.com>
Date: Wed, 15 Apr 2026 19:02:53 +0800
Subject: [PATCH 16/16] =?UTF-8?q?refactor(cqrs):=20=E7=AE=80=E5=8C=96?=
=?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AE=9A=E4=B9=89=E5=B9=B6=E5=88=86=E7=A6=BB?=
=?UTF-8?q?=E6=B5=81=E5=BC=8F=E5=91=BD=E4=BB=A4=E5=92=8C=E6=9F=A5=E8=AF=A2?=
=?UTF-8?q?=E6=8E=A5=E5=8F=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 移除 ICommand 和 ICommand 的空实现体
- 移除 IQuery 和 IStreamQuery 的空实现体
- 移除 INotification、IRequest、IStreamRequest 的空实现体
- 将 IStreamCommand 分离到独立文件中
- 将 IStreamQuery 分离到独立文件中
- 保持所有接口的核心功能不变,仅简化语法结构
---
.../Cqrs/Command/ICommand.cs | 16 ++-------------
.../Cqrs/Command/IStreamCommand.cs | 20 +++++++++++++++++++
.../Cqrs/INotification.cs | 4 +---
GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs | 4 +---
.../Cqrs/IStreamRequest.cs | 4 +---
.../Cqrs/Query/IQuery.cs | 12 +----------
.../Cqrs/Query/IStreamQuery.cs | 20 +++++++++++++++++++
7 files changed, 46 insertions(+), 34 deletions(-)
create mode 100644 GFramework.Cqrs.Abstractions/Cqrs/Command/IStreamCommand.cs
create mode 100644 GFramework.Cqrs.Abstractions/Cqrs/Query/IStreamQuery.cs
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs
index 6eaa1869..9c62f8f5 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Command/ICommand.cs
@@ -5,21 +5,9 @@ namespace GFramework.Cqrs.Abstractions.Cqrs.Command;
/// 命令通常用于修改系统状态。
///
/// 命令响应类型。
-public interface ICommand : IRequest
-{
-}
+public interface ICommand : IRequest;
///
/// 表示一个无显式返回值的 CQRS 命令。
///
-public interface ICommand : ICommand
-{
-}
-
-///
-/// 表示一个流式 CQRS 命令。
-///
-/// 流式响应元素类型。
-public interface IStreamCommand : IStreamRequest
-{
-}
+public interface ICommand : ICommand;
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Command/IStreamCommand.cs b/GFramework.Cqrs.Abstractions/Cqrs/Command/IStreamCommand.cs
new file mode 100644
index 00000000..51323d19
--- /dev/null
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Command/IStreamCommand.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2026 GeWuYou
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace GFramework.Cqrs.Abstractions.Cqrs.Command;
+
+///
+/// 表示一个流式 CQRS 命令。
+///
+/// 流式响应元素类型。
+public interface IStreamCommand : IStreamRequest;
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/INotification.cs b/GFramework.Cqrs.Abstractions/Cqrs/INotification.cs
index 727f9c07..4a2dbb68 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/INotification.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/INotification.cs
@@ -4,6 +4,4 @@ namespace GFramework.Cqrs.Abstractions.Cqrs;
/// 表示一个一对多发布的通知消息。
/// 通知不要求返回值,允许被零个或多个处理器消费。
///
-public interface INotification
-{
-}
+public interface INotification;
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs b/GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs
index 78627e4b..dd6abb62 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/IRequest.cs
@@ -5,6 +5,4 @@ namespace GFramework.Cqrs.Abstractions.Cqrs;
/// 该接口是命令、查询以及其他请求语义的统一基接口。
///
/// 请求响应类型。
-public interface IRequest
-{
-}
+public interface IRequest;
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs b/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs
index 37a211d4..5464459b 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/IStreamRequest.cs
@@ -5,6 +5,4 @@ namespace GFramework.Cqrs.Abstractions.Cqrs;
/// 请求处理器可以逐步产生响应序列,而不是一次性返回完整结果。
///
/// 流式响应元素类型。
-public interface IStreamRequest
-{
-}
+public interface IStreamRequest;
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs
index 9592a9bd..edf5e1a2 100644
--- a/GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Query/IQuery.cs
@@ -5,14 +5,4 @@ namespace GFramework.Cqrs.Abstractions.Cqrs.Query;
/// 查询用于读取数据,不应产生副作用。
///
/// 查询响应类型。
-public interface IQuery : IRequest
-{
-}
-
-///
-/// 表示一个流式 CQRS 查询。
-///
-/// 流式响应元素类型。
-public interface IStreamQuery : IStreamRequest
-{
-}
+public interface IQuery : IRequest;
diff --git a/GFramework.Cqrs.Abstractions/Cqrs/Query/IStreamQuery.cs b/GFramework.Cqrs.Abstractions/Cqrs/Query/IStreamQuery.cs
new file mode 100644
index 00000000..150fb32c
--- /dev/null
+++ b/GFramework.Cqrs.Abstractions/Cqrs/Query/IStreamQuery.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2026 GeWuYou
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+namespace GFramework.Cqrs.Abstractions.Cqrs.Query;
+
+///
+/// 表示一个流式 CQRS 查询。
+///
+/// 流式响应元素类型。
+public interface IStreamQuery : IStreamRequest;