mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-13 06:04:30 +08:00
docs(core): 添加 CQRS 文档并完善相关扩展方法
- 新增 CQRS 核心概念、命令查询处理器使用指南 - 添加管道行为、流式处理和最佳实践说明 - 实现 CQRS 协程扩展方法支持异步命令执行 - 添加 ContextAware 接口的 CQRS 命令查询扩展 - 集成 Microsoft DI 容器依赖注入支持 - 补充架构模块行为测试验证功能完整性 - 扩展 GameContext 测试用例提高代码覆盖率
This commit is contained in:
parent
115fe65e88
commit
088f02d586
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -23,4 +23,6 @@ global using GFramework.Core.Abstractions.StateManagement;
|
|||||||
global using GFramework.Core.Extensions;
|
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;
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -16,4 +16,5 @@ global using System.Collections.Generic;
|
|||||||
global using System.Linq;
|
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;
|
||||||
|
|||||||
@ -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()
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user