docs(core): 添加 CQRS 文档并完善相关扩展方法

- 新增 CQRS 核心概念、命令查询处理器使用指南
- 添加管道行为、流式处理和最佳实践说明
- 实现 CQRS 协程扩展方法支持异步命令执行
- 添加 ContextAware 接口的 CQRS 命令查询扩展
- 集成 Microsoft DI 容器依赖注入支持
- 补充架构模块行为测试验证功能完整性
- 扩展 GameContext 测试用例提高代码覆盖率
This commit is contained in:
GeWuYou 2026-04-15 07:34:01 +08:00
parent 115fe65e88
commit 088f02d586
13 changed files with 190 additions and 17 deletions

View File

@ -2,6 +2,7 @@ using GFramework.Core.Abstractions.Lifecycle;
using GFramework.Core.Abstractions.Model; using GFramework.Core.Abstractions.Model;
using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Abstractions.Utility; using GFramework.Core.Abstractions.Utility;
using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Abstractions.Architectures; namespace GFramework.Core.Abstractions.Architectures;

View File

@ -1,5 +1,6 @@
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Abstractions.Systems; using GFramework.Core.Abstractions.Systems;
using Microsoft.Extensions.DependencyInjection;
namespace GFramework.Core.Abstractions.Ioc; namespace GFramework.Core.Abstractions.Ioc;

View File

@ -77,6 +77,28 @@ public class ArchitectureModulesBehaviorTests
await architecture.DestroyAsync(); await architecture.DestroyAsync();
} }
/// <summary>
/// 验证兼容别名 <c>RegisterMediatorBehavior</c> 仍会把 CQRS 行为接入请求管道。
/// </summary>
[Test]
public async Task RegisterMediatorBehavior_Should_Apply_Pipeline_Behavior_To_Request()
{
var architecture = new ModuleTestArchitecture(target =>
target.RegisterMediatorBehavior<TrackingPipelineBehavior<ModuleBehaviorRequest, string>>());
await architecture.InitializeAsync();
var response = await architecture.Context.SendRequestAsync(new ModuleBehaviorRequest());
Assert.Multiple(() =>
{
Assert.That(response, Is.EqualTo("handled"));
Assert.That(TrackingPipelineBehavior<ModuleBehaviorRequest, string>.InvocationCount, Is.EqualTo(1));
});
await architecture.DestroyAsync();
}
/// <summary> /// <summary>
/// 用于测试模块行为的最小架构实现。 /// 用于测试模块行为的最小架构实现。
/// </summary> /// </summary>

View File

@ -394,45 +394,106 @@ public class TestArchitectureContext : IArchitectureContext
{ {
} }
/// <summary>
/// 测试桩:异步发送统一 CQRS 请求。
/// </summary>
/// <typeparam name="TResponse">响应类型。</typeparam>
/// <param name="request">要发送的请求。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>请求响应任务。</returns>
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
public ValueTask<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> request, public ValueTask<TResponse> SendRequestAsync<TResponse>(IRequest<TResponse> request,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary>
/// 测试桩:同步发送统一 CQRS 请求。
/// </summary>
/// <typeparam name="TResponse">响应类型。</typeparam>
/// <param name="request">要发送的请求。</param>
/// <returns>请求响应。</returns>
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
public TResponse SendRequest<TResponse>(IRequest<TResponse> request) public TResponse SendRequest<TResponse>(IRequest<TResponse> request)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary>
/// 测试桩:异步发送 CQRS 命令并返回响应。
/// </summary>
/// <typeparam name="TResponse">命令响应类型。</typeparam>
/// <param name="command">要发送的命令。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>命令响应任务。</returns>
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
public ValueTask<TResponse> SendCommandAsync<TResponse>(Abstractions.Cqrs.Command.ICommand<TResponse> command, public ValueTask<TResponse> SendCommandAsync<TResponse>(Abstractions.Cqrs.Command.ICommand<TResponse> command,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary>
/// 测试桩:同步发送 CQRS 命令并返回响应。
/// </summary>
/// <typeparam name="TResponse">命令响应类型。</typeparam>
/// <param name="command">要发送的命令。</param>
/// <returns>命令响应。</returns>
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
public TResponse SendCommand<TResponse>(Abstractions.Cqrs.Command.ICommand<TResponse> command) public TResponse SendCommand<TResponse>(Abstractions.Cqrs.Command.ICommand<TResponse> command)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary>
/// 测试桩:异步发送 CQRS 查询并返回结果。
/// </summary>
/// <typeparam name="TResponse">查询结果类型。</typeparam>
/// <param name="query">要发送的查询。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>查询结果任务。</returns>
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
public ValueTask<TResponse> SendQueryAsync<TResponse>(Abstractions.Cqrs.Query.IQuery<TResponse> query, public ValueTask<TResponse> SendQueryAsync<TResponse>(Abstractions.Cqrs.Query.IQuery<TResponse> query,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary>
/// 测试桩:同步发送 CQRS 查询并返回结果。
/// </summary>
/// <typeparam name="TResponse">查询结果类型。</typeparam>
/// <param name="query">要发送的查询。</param>
/// <returns>查询结果。</returns>
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
public TResponse SendQuery<TResponse>(Abstractions.Cqrs.Query.IQuery<TResponse> query) public TResponse SendQuery<TResponse>(Abstractions.Cqrs.Query.IQuery<TResponse> query)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary>
/// 测试桩:异步发布 CQRS 通知。
/// </summary>
/// <typeparam name="TNotification">通知类型。</typeparam>
/// <param name="notification">要发布的通知。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>通知发布任务。</returns>
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
public ValueTask PublishAsync<TNotification>(TNotification notification, public ValueTask PublishAsync<TNotification>(TNotification notification,
CancellationToken cancellationToken = default) where TNotification : INotification CancellationToken cancellationToken = default) where TNotification : INotification
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary>
/// 测试桩:创建 CQRS 流式请求响应序列。
/// </summary>
/// <typeparam name="TResponse">流式响应元素类型。</typeparam>
/// <param name="request">流式请求。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>异步响应流。</returns>
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
public IAsyncEnumerable<TResponse> CreateStream<TResponse>( public IAsyncEnumerable<TResponse> CreateStream<TResponse>(
IStreamRequest<TResponse> request, IStreamRequest<TResponse> request,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
@ -440,12 +501,28 @@ public class TestArchitectureContext : IArchitectureContext
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary>
/// 测试桩:异步发送无返回值 CQRS 命令。
/// </summary>
/// <typeparam name="TCommand">命令类型。</typeparam>
/// <param name="command">要发送的命令。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>命令发送任务。</returns>
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default) public ValueTask SendAsync<TCommand>(TCommand command, CancellationToken cancellationToken = default)
where TCommand : IRequest<Unit> where TCommand : IRequest<Unit>
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
/// <summary>
/// 测试桩:异步发送带返回值的 CQRS 请求。
/// </summary>
/// <typeparam name="TResponse">响应类型。</typeparam>
/// <param name="command">要发送的请求。</param>
/// <param name="cancellationToken">取消令牌。</param>
/// <returns>请求响应任务。</returns>
/// <exception cref="NotImplementedException">该测试桩未实现此成员。</exception>
public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> command, public ValueTask<TResponse> SendAsync<TResponse>(IRequest<TResponse> command,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {

View File

@ -66,6 +66,32 @@ public class CqrsCoroutineExtensionsTests
Assert.That(exception, Is.SameAs(expectedException)); Assert.That(exception, Is.SameAs(expectedException));
} }
/// <summary>
/// 验证 SendCommandCoroutine 在提供错误回调时也会传递解包后的原始异常,
/// 避免回调路径暴露 <see cref="AggregateException" />。
/// </summary>
[Test]
public void SendCommandCoroutine_Should_Forward_Inner_Exception_To_Error_Handler()
{
var command = new TestCommand("Test");
var contextAware = new TestContextAware();
var expectedException = new InvalidOperationException("Command failed.");
Exception? capturedException = null;
contextAware.MockContext
.Setup(ctx => ctx.SendAsync(command, It.IsAny<CancellationToken>()))
.Returns(new ValueTask(Task.FromException(expectedException)));
var coroutine = CqrsCoroutineExtensions.SendCommandCoroutine(
contextAware,
command,
exception => capturedException = exception);
Assert.That(coroutine.MoveNext(), Is.True);
Assert.That(coroutine.MoveNext(), Is.False);
Assert.That(capturedException, Is.SameAs(expectedException));
}
/// <summary> /// <summary>
/// 测试用的简单命令类 /// 测试用的简单命令类
/// </summary> /// </summary>

View File

@ -24,3 +24,5 @@ global using GFramework.Core.Extensions;
global using GFramework.Core.Property; global using GFramework.Core.Property;
global using GFramework.Core.StateManagement; global using GFramework.Core.StateManagement;
global using GFramework.Core.Abstractions.Property; global using GFramework.Core.Abstractions.Property;
global using Microsoft.Extensions.DependencyInjection;
global using Moq;

View File

@ -19,6 +19,13 @@ public static class CqrsCoroutineExtensions
/// <param name="command">要发送的命令对象。</param> /// <param name="command">要发送的命令对象。</param>
/// <param name="onError">发生异常时的回调处理函数。</param> /// <param name="onError">发生异常时的回调处理函数。</param>
/// <returns>协程枚举器,用于协程执行。</returns> /// <returns>协程枚举器,用于协程执行。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="command" /> 为 <see langword="null" /> 时抛出。
/// </exception>
/// <remarks>
/// 当底层命令调度失败时,该扩展会把底层异常解包后传给 <paramref name="onError" />
/// 或在未提供回调时重新抛出同一个异常实例,避免两条失败路径暴露不同的异常类型。
/// </remarks>
public static IEnumerator<IYieldInstruction> SendCommandCoroutine<TCommand>( public static IEnumerator<IYieldInstruction> SendCommandCoroutine<TCommand>(
this IContextAware contextAware, this IContextAware contextAware,
TCommand command, TCommand command,
@ -35,9 +42,10 @@ public static class CqrsCoroutineExtensions
if (!task.IsFaulted) if (!task.IsFaulted)
yield break; yield break;
var exception = task.Exception!.InnerException ?? task.Exception;
if (onError != null) if (onError != null)
onError.Invoke(task.Exception!); onError.Invoke(exception);
else else
throw task.Exception!.InnerException ?? task.Exception; throw exception;
} }
} }

View File

@ -15,6 +15,9 @@ public static class ContextAwareCqrsCommandExtensions
/// <param name="contextAware">实现 <see cref="IContextAware" /> 接口的对象。</param> /// <param name="contextAware">实现 <see cref="IContextAware" /> 接口的对象。</param>
/// <param name="command">要发送的命令对象。</param> /// <param name="command">要发送的命令对象。</param>
/// <returns>命令执行结果。</returns> /// <returns>命令执行结果。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="command" /> 为 <see langword="null" /> 时抛出。
/// </exception>
public static TResponse SendCommand<TResponse>(this IContextAware contextAware, ICommand<TResponse> command) public static TResponse SendCommand<TResponse>(this IContextAware contextAware, ICommand<TResponse> command)
{ {
ArgumentNullException.ThrowIfNull(contextAware); ArgumentNullException.ThrowIfNull(contextAware);
@ -31,6 +34,9 @@ public static class ContextAwareCqrsCommandExtensions
/// <param name="command">要发送的命令对象。</param> /// <param name="command">要发送的命令对象。</param>
/// <param name="cancellationToken">取消令牌,用于取消操作。</param> /// <param name="cancellationToken">取消令牌,用于取消操作。</param>
/// <returns>包含命令执行结果的 <see cref="ValueTask{TResult}" />。</returns> /// <returns>包含命令执行结果的 <see cref="ValueTask{TResult}" />。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="command" /> 为 <see langword="null" /> 时抛出。
/// </exception>
public static ValueTask<TResponse> SendCommandAsync<TResponse>( public static ValueTask<TResponse> SendCommandAsync<TResponse>(
this IContextAware contextAware, this IContextAware contextAware,
ICommand<TResponse> command, ICommand<TResponse> command,

View File

@ -17,6 +17,9 @@ public static class ContextAwareCqrsExtensions
/// <param name="request">要发送的请求。</param> /// <param name="request">要发送的请求。</param>
/// <param name="cancellationToken">取消令牌。</param> /// <param name="cancellationToken">取消令牌。</param>
/// <returns>请求结果。</returns> /// <returns>请求结果。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="request" /> 为 <see langword="null" /> 时抛出。
/// </exception>
public static ValueTask<TResponse> SendRequestAsync<TResponse>( public static ValueTask<TResponse> SendRequestAsync<TResponse>(
this IContextAware contextAware, this IContextAware contextAware,
IRequest<TResponse> request, IRequest<TResponse> request,
@ -35,6 +38,9 @@ public static class ContextAwareCqrsExtensions
/// <param name="contextAware">实现 <see cref="IContextAware" /> 接口的对象。</param> /// <param name="contextAware">实现 <see cref="IContextAware" /> 接口的对象。</param>
/// <param name="request">要发送的请求。</param> /// <param name="request">要发送的请求。</param>
/// <returns>请求结果。</returns> /// <returns>请求结果。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="request" /> 为 <see langword="null" /> 时抛出。
/// </exception>
public static TResponse SendRequest<TResponse>(this IContextAware contextAware, IRequest<TResponse> request) public static TResponse SendRequest<TResponse>(this IContextAware contextAware, IRequest<TResponse> request)
{ {
ArgumentNullException.ThrowIfNull(contextAware); ArgumentNullException.ThrowIfNull(contextAware);
@ -51,6 +57,9 @@ public static class ContextAwareCqrsExtensions
/// <param name="notification">要发布的通知。</param> /// <param name="notification">要发布的通知。</param>
/// <param name="cancellationToken">取消令牌。</param> /// <param name="cancellationToken">取消令牌。</param>
/// <returns>异步任务。</returns> /// <returns>异步任务。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="notification" /> 为 <see langword="null" /> 时抛出。
/// </exception>
public static ValueTask PublishAsync<TNotification>( public static ValueTask PublishAsync<TNotification>(
this IContextAware contextAware, this IContextAware contextAware,
TNotification notification, TNotification notification,
@ -71,6 +80,9 @@ public static class ContextAwareCqrsExtensions
/// <param name="request">流式请求。</param> /// <param name="request">流式请求。</param>
/// <param name="cancellationToken">取消令牌。</param> /// <param name="cancellationToken">取消令牌。</param>
/// <returns>异步响应流。</returns> /// <returns>异步响应流。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="request" /> 为 <see langword="null" /> 时抛出。
/// </exception>
public static IAsyncEnumerable<TResponse> CreateStream<TResponse>( public static IAsyncEnumerable<TResponse> CreateStream<TResponse>(
this IContextAware contextAware, this IContextAware contextAware,
IStreamRequest<TResponse> request, IStreamRequest<TResponse> request,
@ -90,6 +102,9 @@ public static class ContextAwareCqrsExtensions
/// <param name="command">要发送的命令。</param> /// <param name="command">要发送的命令。</param>
/// <param name="cancellationToken">取消令牌。</param> /// <param name="cancellationToken">取消令牌。</param>
/// <returns>异步任务。</returns> /// <returns>异步任务。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="command" /> 为 <see langword="null" /> 时抛出。
/// </exception>
public static ValueTask SendAsync<TCommand>( public static ValueTask SendAsync<TCommand>(
this IContextAware contextAware, this IContextAware contextAware,
TCommand command, TCommand command,
@ -110,6 +125,9 @@ public static class ContextAwareCqrsExtensions
/// <param name="command">要发送的命令。</param> /// <param name="command">要发送的命令。</param>
/// <param name="cancellationToken">取消令牌。</param> /// <param name="cancellationToken">取消令牌。</param>
/// <returns>命令执行结果。</returns> /// <returns>命令执行结果。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="command" /> 为 <see langword="null" /> 时抛出。
/// </exception>
public static ValueTask<TResponse> SendAsync<TResponse>( public static ValueTask<TResponse> SendAsync<TResponse>(
this IContextAware contextAware, this IContextAware contextAware,
IRequest<TResponse> command, IRequest<TResponse> command,

View File

@ -15,6 +15,9 @@ public static class ContextAwareCqrsQueryExtensions
/// <param name="contextAware">实现 <see cref="IContextAware" /> 接口的对象。</param> /// <param name="contextAware">实现 <see cref="IContextAware" /> 接口的对象。</param>
/// <param name="query">要发送的查询对象。</param> /// <param name="query">要发送的查询对象。</param>
/// <returns>查询结果。</returns> /// <returns>查询结果。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="query" /> 为 <see langword="null" /> 时抛出。
/// </exception>
public static TResponse SendQuery<TResponse>(this IContextAware contextAware, IQuery<TResponse> query) public static TResponse SendQuery<TResponse>(this IContextAware contextAware, IQuery<TResponse> query)
{ {
ArgumentNullException.ThrowIfNull(contextAware); ArgumentNullException.ThrowIfNull(contextAware);
@ -31,6 +34,9 @@ public static class ContextAwareCqrsQueryExtensions
/// <param name="query">要发送的查询对象。</param> /// <param name="query">要发送的查询对象。</param>
/// <param name="cancellationToken">取消令牌,用于取消操作。</param> /// <param name="cancellationToken">取消令牌,用于取消操作。</param>
/// <returns>包含查询结果的 <see cref="ValueTask{TResult}" />。</returns> /// <returns>包含查询结果的 <see cref="ValueTask{TResult}" />。</returns>
/// <exception cref="ArgumentNullException">
/// 当 <paramref name="contextAware" /> 或 <paramref name="query" /> 为 <see langword="null" /> 时抛出。
/// </exception>
public static ValueTask<TResponse> SendQueryAsync<TResponse>( public static ValueTask<TResponse> SendQueryAsync<TResponse>(
this IContextAware contextAware, this IContextAware contextAware,
IQuery<TResponse> query, IQuery<TResponse> query,

View File

@ -17,3 +17,4 @@ global using System.Linq;
global using System.Threading; global using System.Threading;
global using System.Threading.Tasks; global using System.Threading.Tasks;
global using System.Threading.Channels; global using System.Threading.Channels;
global using Microsoft.Extensions.DependencyInjection;

View File

@ -2,6 +2,9 @@
using GFramework.Core.Abstractions.Cqrs.Command; using GFramework.Core.Abstractions.Cqrs.Command;
using GFramework.Core.Abstractions.Cqrs.Query; 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;
namespace GFramework.Godot.Coroutine; namespace GFramework.Godot.Coroutine;
@ -26,7 +29,7 @@ public static class ContextAwareCoroutineExtensions
string? tag = null, string? tag = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return Core.Cqrs.Extensions.ContextAwareCqrsCommandExtensions return ContextAwareCqrsCommandExtensions
.SendCommandAsync(contextAware, command, cancellationToken) .SendCommandAsync(contextAware, command, cancellationToken)
.AsTask() .AsTask()
.ToCoroutineEnumerator() .ToCoroutineEnumerator()
@ -50,7 +53,7 @@ public static class ContextAwareCoroutineExtensions
string? tag = null, string? tag = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return Core.Cqrs.Extensions.ContextAwareCqrsCommandExtensions return ContextAwareCqrsCommandExtensions
.SendCommandAsync(contextAware, command, cancellationToken) .SendCommandAsync(contextAware, command, cancellationToken)
.AsTask() .AsTask()
.ToCoroutineEnumerator() .ToCoroutineEnumerator()
@ -74,7 +77,7 @@ public static class ContextAwareCoroutineExtensions
string? tag = null, string? tag = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return Core.Cqrs.Extensions.ContextAwareCqrsQueryExtensions return ContextAwareCqrsQueryExtensions
.SendQueryAsync(contextAware, query, cancellationToken) .SendQueryAsync(contextAware, query, cancellationToken)
.AsTask() .AsTask()
.ToCoroutineEnumerator() .ToCoroutineEnumerator()
@ -97,7 +100,7 @@ public static class ContextAwareCoroutineExtensions
string? tag = null, string? tag = null,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
return Core.Cqrs.Extensions.ContextAwareCqrsExtensions return ContextAwareCqrsExtensions
.PublishAsync(contextAware, notification, cancellationToken) .PublishAsync(contextAware, notification, cancellationToken)
.AsTask() .AsTask()
.ToCoroutineEnumerator() .ToCoroutineEnumerator()

View File

@ -204,22 +204,24 @@ public async Task<List<ScoreData>> GetHighScores()
### 注册处理器 ### 注册处理器
在架构中注册 CQRS 行为并让处理器自动扫描注册 在架构中注册 CQRS 行为;默认会自动扫描当前架构所在程序集和 `GFramework.Core` 程序集中的处理器
```csharp ```csharp
public class GameArchitecture : Architecture public class GameArchitecture : Architecture
{ {
protected override void Init() protected override void OnInitialize()
{ {
// 注册通用开放泛型行为 // 注册通用开放泛型行为
RegisterCqrsPipelineBehavior<LoggingBehavior<,>>(); RegisterCqrsPipelineBehavior<LoggingBehavior<,>>();
RegisterCqrsPipelineBehavior<PerformanceBehavior<,>>(); RegisterCqrsPipelineBehavior<PerformanceBehavior<,>>();
// 处理器会自动通过依赖注入注册 // 默认只自动扫描当前架构程序集和 GFramework.Core 程序集中的处理器
} }
} }
``` ```
如果处理器位于其他模块或扩展程序集中,需要额外接入对应程序集的处理器注册,而不是依赖默认扫描。
`RegisterCqrsPipelineBehavior<TBehavior>()` 是推荐入口;旧的 `RegisterMediatorBehavior<TBehavior>()` `RegisterCqrsPipelineBehavior<TBehavior>()` 是推荐入口;旧的 `RegisterMediatorBehavior<TBehavior>()`
仅作为兼容名称保留。当前接口支持两种形式: 仅作为兼容名称保留。当前接口支持两种形式:
@ -338,8 +340,8 @@ public class LoggingBehavior<TMessage, TResponse> : IPipelineBehavior<TMessage,
{ {
public async ValueTask<TResponse> Handle( public async ValueTask<TResponse> Handle(
TMessage message, TMessage message,
CancellationToken cancellationToken, MessageHandlerDelegate<TMessage, TResponse> next,
MessageHandlerDelegate<TMessage, TResponse> next) CancellationToken cancellationToken)
{ {
var messageName = message.GetType().Name; var messageName = message.GetType().Name;
Console.WriteLine($"[开始] {messageName}"); Console.WriteLine($"[开始] {messageName}");
@ -358,8 +360,8 @@ public class PerformanceBehavior<TMessage, TResponse> : IPipelineBehavior<TMessa
{ {
public async ValueTask<TResponse> Handle( public async ValueTask<TResponse> Handle(
TMessage message, TMessage message,
CancellationToken cancellationToken, MessageHandlerDelegate<TMessage, TResponse> next,
MessageHandlerDelegate<TMessage, TResponse> next) CancellationToken cancellationToken)
{ {
var stopwatch = Stopwatch.StartNew(); var stopwatch = Stopwatch.StartNew();
@ -390,8 +392,8 @@ public class ValidationBehavior<TMessage, TResponse> : IPipelineBehavior<TMessag
{ {
public async ValueTask<TResponse> Handle( public async ValueTask<TResponse> Handle(
TMessage message, TMessage message,
CancellationToken cancellationToken, MessageHandlerDelegate<TMessage, TResponse> next,
MessageHandlerDelegate<TMessage, TResponse> next) CancellationToken cancellationToken)
{ {
// 验证输入 // 验证输入
if (message is IValidatable validatable) if (message is IValidatable validatable)