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 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); + private static readonly ConcurrentDictionary<(Type RequestType, Type ResponseType), RequestInvoker> + RequestInvokers = new(); + + private static readonly ConcurrentDictionary<(Type RequestType, Type ResponseType), RequestPipelineInvoker> + RequestPipelineInvokers = new(); - private static readonly ConcurrentDictionary<(Type RequestType, Type ResponseType), RequestInvoker> RequestInvokers = new(); - 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(); + + private static readonly ConcurrentDictionary<(Type RequestType, Type ResponseType), StreamInvoker> StreamInvokers = + new(); + + /// + /// 发布通知到所有已注册处理器。 + /// + /// 通知类型。 + /// 当前架构上下文,用于上下文感知处理器注入。 + /// 通知对象。 + /// 取消令牌。 + public async ValueTask PublishAsync( + IArchitectureContext context, + TNotification notification, + CancellationToken cancellationToken = default) + where TNotification : INotification + { + ArgumentNullException.ThrowIfNull(context); + ArgumentNullException.ThrowIfNull(notification); + + var notificationType = notification.GetType(); + var handlerType = typeof(INotificationHandler<>).MakeGenericType(notificationType); + var handlers = container.GetAll(handlerType); + + if (handlers.Count == 0) + { + logger.Debug($"No CQRS notification handler registered for {notificationType.FullName}."); + return; + } + + var invoker = NotificationInvokers.GetOrAdd( + notificationType, + CreateNotificationInvoker); + + foreach (var handler in handlers) + { + PrepareHandler(handler, context); + await invoker(handler, notification, cancellationToken); + } + } /// /// 发送请求并返回结果。 /// /// 响应类型。 + /// 当前架构上下文,用于上下文感知处理器注入。 /// 请求对象。 /// 取消令牌。 /// 请求响应。 public async ValueTask SendAsync( + IArchitectureContext context, IRequest request, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(request); var requestType = request.GetType(); @@ -50,12 +86,12 @@ internal sealed class CqrsDispatcher( ?? throw new InvalidOperationException( $"No CQRS request handler registered for {requestType.FullName}."); - PrepareHandler(handler); + PrepareHandler(handler, context); var behaviorType = typeof(IPipelineBehavior<,>).MakeGenericType(requestType, typeof(TResponse)); var behaviors = container.GetAll(behaviorType); foreach (var behavior in behaviors) - PrepareHandler(behavior); + PrepareHandler(behavior, context); if (behaviors.Count == 0) { @@ -75,51 +111,20 @@ internal sealed class CqrsDispatcher( return pipelineResult is null ? default! : (TResponse)pipelineResult; } - /// - /// 发布通知到所有已注册处理器。 - /// - /// 通知类型。 - /// 通知对象。 - /// 取消令牌。 - public async ValueTask PublishAsync( - TNotification notification, - CancellationToken cancellationToken = default) - where TNotification : INotification - { - ArgumentNullException.ThrowIfNull(notification); - - var notificationType = notification.GetType(); - var handlerType = typeof(INotificationHandler<>).MakeGenericType(notificationType); - var handlers = container.GetAll(handlerType); - - if (handlers.Count == 0) - { - logger.Debug($"No CQRS notification handler registered for {notificationType.FullName}."); - return; - } - - var invoker = NotificationInvokers.GetOrAdd( - notificationType, - CreateNotificationInvoker); - - foreach (var handler in handlers) - { - PrepareHandler(handler); - await invoker(handler, notification, cancellationToken); - } - } - /// /// 创建流式请求并返回异步响应序列。 /// /// 响应元素类型。 + /// 当前架构上下文,用于上下文感知处理器注入。 /// 流式请求对象。 /// 取消令牌。 /// 异步响应序列。 public IAsyncEnumerable CreateStream( + IArchitectureContext context, IStreamRequest request, CancellationToken cancellationToken = default) { + ArgumentNullException.ThrowIfNull(context); ArgumentNullException.ThrowIfNull(request); var requestType = request.GetType(); @@ -128,7 +133,7 @@ internal sealed class CqrsDispatcher( ?? throw new InvalidOperationException( $"No CQRS stream handler registered for {requestType.FullName}."); - PrepareHandler(handler); + PrepareHandler(handler, context); var invoker = StreamInvokers.GetOrAdd( (requestType, typeof(TResponse)), @@ -141,7 +146,8 @@ internal sealed class CqrsDispatcher( /// 为上下文感知处理器注入当前架构上下文。 /// /// 处理器实例。 - 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;