feat(cqrs): 添加CQRS运行时模块和兼容性扩展

- 新增ContextAwareMediatorCommandExtensions提供命令扩展方法的兼容性别名
- 新增ContextAwareMediatorExtensions提供CQRS统一接口扩展方法的兼容性别名
- 新增ContextAwareMediatorQueryExtensions提供查询扩展方法的兼容性别名
- 添加CqrsRuntimeModule用于注册CQRS运行时和处理器注册器到依赖注入容器
- 更新IArchitectureContext接口添加新版CQRS请求、命令、查询和通知的统一入口
- 添加架构上下文的CQRS处理器注册相关单元测试
- 配置项目文件以支持多目标框架和包引用管理
This commit is contained in:
GeWuYou 2026-04-15 19:42:08 +08:00
parent f7b4ae9995
commit a80ff59631
29 changed files with 129 additions and 86 deletions

View File

@ -15,7 +15,7 @@ namespace GFramework.Core.Abstractions.Architectures;
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <para>旧的 <c>GFramework.Core.Abstractions.Command</c> 与 <c>GFramework.Core.Abstractions.Query</c> 契约会继续通过原有 Command/Query Executor 路径执行,以保证存量代码兼容。</para> /// <para>旧的 <c>GFramework.Core.Abstractions.Command</c> 与 <c>GFramework.Core.Abstractions.Query</c> 契约会继续通过原有 Command/Query Executor 路径执行,以保证存量代码兼容。</para>
/// <para>新的 <c>GFramework.Core.Abstractions.Cqrs</c> 契约由内置 CQRS dispatcher 统一处理,支持 request pipeline、notification publish 与 stream request。</para> /// <para>新的 <c>GFramework.Cqrs.Abstractions.Cqrs</c> 契约由内置 CQRS dispatcher 统一处理,支持 request pipeline、notification publish 与 stream request。</para>
/// <para>新功能优先使用 <see cref="SendRequestAsync{TResponse}(IRequest{TResponse},CancellationToken)" />、<see cref="SendAsync{TCommand}(TCommand,CancellationToken)" /> 与对应的 CQRS Command/Query 重载;迁移旧代码时可先保留旧入口,再逐步替换为 CQRS 请求模型。</para> /// <para>新功能优先使用 <see cref="SendRequestAsync{TResponse}(IRequest{TResponse},CancellationToken)" />、<see cref="SendAsync{TCommand}(TCommand,CancellationToken)" /> 与对应的 CQRS Command/Query 重载;迁移旧代码时可先保留旧入口,再逐步替换为 CQRS 请求模型。</para>
/// </remarks> /// </remarks>
public interface IArchitectureContext public interface IArchitectureContext
@ -175,7 +175,7 @@ public interface IArchitectureContext
/// <param name="query">要发送的 CQRS 查询。</param> /// <param name="query">要发送的 CQRS 查询。</param>
/// <returns>查询结果。</returns> /// <returns>查询结果。</returns>
/// <remarks> /// <remarks>
/// 这是迁移后的推荐查询入口。新查询应优先实现 <c>GFramework.Core.Abstractions.Cqrs.Query.IQuery&lt;TResponse&gt;</c>。 /// 这是迁移后的推荐查询入口。新查询应优先实现 <c>GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery&lt;TResponse&gt;</c>。
/// </remarks> /// </remarks>
TResponse SendQuery<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query); TResponse SendQuery<TResponse>(GFramework.Cqrs.Abstractions.Cqrs.Query.IQuery<TResponse> query);

View File

@ -1,8 +1,8 @@
using System.Reflection; using System.Reflection;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures; using GFramework.Core.Architectures;
using GFramework.Core.Logging; using GFramework.Core.Logging;
using GFramework.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Tests.Architectures; namespace GFramework.Core.Tests.Architectures;

View File

@ -1,6 +1,7 @@
using System.ComponentModel; using System.ComponentModel;
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs.Command; using GFramework.Cqrs.Abstractions.Cqrs.Command;
using GFramework.Cqrs.Extensions;
namespace GFramework.Core.Extensions; namespace GFramework.Core.Extensions;

View File

@ -1,6 +1,7 @@
using System.ComponentModel; using System.ComponentModel;
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Extensions;
namespace GFramework.Core.Extensions; namespace GFramework.Core.Extensions;

View File

@ -1,6 +1,7 @@
using System.ComponentModel; using System.ComponentModel;
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs.Query; using GFramework.Cqrs.Abstractions.Cqrs.Query;
using GFramework.Cqrs.Extensions;
namespace GFramework.Core.Extensions; namespace GFramework.Core.Extensions;

View File

@ -10,6 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\GFramework.Cqrs.Abstractions\GFramework.Cqrs.Abstractions.csproj"/> <ProjectReference Include="..\GFramework.Cqrs.Abstractions\GFramework.Cqrs.Abstractions.csproj"/>
<ProjectReference Include="..\GFramework.Cqrs\GFramework.Cqrs.csproj"/>
<ProjectReference Include="..\$(AssemblyName).Abstractions\$(AssemblyName).Abstractions.csproj"/> <ProjectReference Include="..\$(AssemblyName).Abstractions\$(AssemblyName).Abstractions.csproj"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,8 +1,8 @@
using GFramework.Core.Abstractions.Architectures; using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Cqrs; using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Cqrs.Internal;
using GFramework.Core.Logging; using GFramework.Core.Logging;
using GFramework.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Services.Modules; namespace GFramework.Core.Services.Modules;
@ -37,11 +37,12 @@ public sealed class CqrsRuntimeModule : IServiceModule
{ {
ArgumentNullException.ThrowIfNull(container); ArgumentNullException.ThrowIfNull(container);
var dispatcherLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(CqrsDispatcher)); var dispatcherLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(nameof(DefaultCqrsHandlerRegistrar)); var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsHandlerRegistrar");
container.Register<ICqrsRuntime>(new CqrsDispatcher(container, dispatcherLogger)); container.Register<ICqrsRuntime>(CqrsRuntimeFactory.CreateRuntime(container, dispatcherLogger));
container.Register<ICqrsHandlerRegistrar>(new DefaultCqrsHandlerRegistrar(container, registrarLogger)); container.Register<ICqrsHandlerRegistrar>(
CqrsRuntimeFactory.CreateHandlerRegistrar(container, registrarLogger));
} }
/// <summary> /// <summary>

View File

@ -1,4 +1,3 @@
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures; using GFramework.Core.Architectures;
using GFramework.Core.Ioc; using GFramework.Core.Ioc;

View File

@ -13,7 +13,7 @@
using GFramework.Cqrs.Abstractions.Cqrs.Command; using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Cqrs.Command; namespace GFramework.Cqrs.Command;
/// <summary> /// <summary>
/// 表示一个基础命令类,用于处理带有输入和响应的命令模式实现。 /// 表示一个基础命令类,用于处理带有输入和响应的命令模式实现。

View File

@ -1,4 +1,4 @@
namespace GFramework.Core.Abstractions.Cqrs; namespace GFramework.Cqrs;
/// <summary> /// <summary>
/// 声明程序集内可供运行时直接调用的 CQRS 处理器注册器类型。 /// 声明程序集内可供运行时直接调用的 CQRS 处理器注册器类型。

View File

@ -0,0 +1,45 @@
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Internal;
namespace GFramework.Cqrs;
/// <summary>
/// 提供 CQRS runtime 默认实现的跨程序集创建入口。
/// </summary>
/// <remarks>
/// <see cref="GFramework.Core" /> 需要在不暴露内部实现细节的前提下接入默认 CQRS runtime
/// 因此通过该工厂返回抽象接口,而不是直接公开内部 dispatcher / registrar 类型。
/// </remarks>
public static class CqrsRuntimeFactory
{
/// <summary>
/// 创建默认 CQRS runtime 分发器。
/// </summary>
/// <param name="container">目标依赖注入容器。</param>
/// <param name="logger">用于 runtime 诊断的日志器。</param>
/// <returns>默认 CQRS runtime。</returns>
public static ICqrsRuntime CreateRuntime(IIocContainer container, ILogger logger)
{
ArgumentNullException.ThrowIfNull(container);
ArgumentNullException.ThrowIfNull(logger);
return new CqrsDispatcher(container, logger);
}
/// <summary>
/// 创建默认 CQRS 处理器注册器。
/// </summary>
/// <param name="container">目标依赖注入容器。</param>
/// <param name="logger">用于注册阶段诊断的日志器。</param>
/// <returns>默认 CQRS handler registrar。</returns>
public static ICqrsHandlerRegistrar CreateHandlerRegistrar(IIocContainer container, ILogger logger)
{
ArgumentNullException.ThrowIfNull(container);
ArgumentNullException.ThrowIfNull(logger);
return new DefaultCqrsHandlerRegistrar(container, logger);
}
}

View File

@ -1,7 +1,7 @@
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs.Command; using GFramework.Cqrs.Abstractions.Cqrs.Command;
namespace GFramework.Core.Extensions; namespace GFramework.Cqrs.Extensions;
/// <summary> /// <summary>
/// 提供对 <see cref="IContextAware" /> 接口的 CQRS 命令扩展方法。 /// 提供对 <see cref="IContextAware" /> 接口的 CQRS 命令扩展方法。

View File

@ -1,7 +1,7 @@
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Extensions; namespace GFramework.Cqrs.Extensions;
/// <summary> /// <summary>
/// 提供对 <see cref="IContextAware" /> 接口的 CQRS 统一扩展方法。 /// 提供对 <see cref="IContextAware" /> 接口的 CQRS 统一扩展方法。

View File

@ -1,7 +1,7 @@
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs.Query; using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Extensions; namespace GFramework.Cqrs.Extensions;
/// <summary> /// <summary>
/// 提供对 <see cref="IContextAware" /> 接口的 CQRS 查询扩展方法。 /// 提供对 <see cref="IContextAware" /> 接口的 CQRS 查询扩展方法。

View File

@ -11,6 +11,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\GFramework.Cqrs.Abstractions\GFramework.Cqrs.Abstractions.csproj"/> <ProjectReference Include="..\GFramework.Cqrs.Abstractions\GFramework.Cqrs.Abstractions.csproj"/>
<ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,6 @@
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Threading;
global using System.Threading.Tasks;
global using Microsoft.Extensions.DependencyInjection;

View File

@ -1,6 +1,6 @@
using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Logging;
namespace GFramework.Core.Abstractions.Cqrs; namespace GFramework.Cqrs;
/// <summary> /// <summary>
/// 定义由源码生成器产出的 CQRS 处理器注册器契约。 /// 定义由源码生成器产出的 CQRS 处理器注册器契约。

View File

@ -7,7 +7,7 @@ using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Internal; namespace GFramework.Cqrs.Internal;
/// <summary> /// <summary>
/// GFramework 自有 CQRS 运行时分发器。 /// GFramework 自有 CQRS 运行时分发器。

View File

@ -1,10 +1,9 @@
using System.Reflection; using System.Reflection;
using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Logging;
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Internal; namespace GFramework.Cqrs.Internal;
/// <summary> /// <summary>
/// 在架构初始化期间扫描并注册 CQRS 处理器。 /// 在架构初始化期间扫描并注册 CQRS 处理器。

View File

@ -3,7 +3,7 @@ using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Logging;
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Cqrs.Internal; namespace GFramework.Cqrs.Internal;
/// <summary> /// <summary>
/// 默认的 CQRS 处理器注册器实现。 /// 默认的 CQRS 处理器注册器实现。

View File

@ -14,7 +14,7 @@
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs.Notification; using GFramework.Cqrs.Abstractions.Cqrs.Notification;
namespace GFramework.Core.Cqrs.Notification; namespace GFramework.Cqrs.Notification;
/// <summary> /// <summary>
/// 表示一个基础通知类,用于处理带有输入的通知模式实现。 /// 表示一个基础通知类,用于处理带有输入的通知模式实现。

View File

@ -13,7 +13,7 @@
using GFramework.Cqrs.Abstractions.Cqrs.Query; using GFramework.Cqrs.Abstractions.Cqrs.Query;
namespace GFramework.Core.Cqrs.Query; namespace GFramework.Cqrs.Query;
/// <summary> /// <summary>
/// 表示一个基础查询类,用于处理带有输入和响应的查询模式实现。 /// 表示一个基础查询类,用于处理带有输入和响应的查询模式实现。

View File

@ -14,7 +14,7 @@
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs.Request; using GFramework.Cqrs.Abstractions.Cqrs.Request;
namespace GFramework.Core.Cqrs.Request; namespace GFramework.Cqrs.Request;
/// <summary> /// <summary>
/// 表示一个基础请求类,用于处理带有输入和响应的请求模式实现。 /// 表示一个基础请求类,用于处理带有输入和响应的请求模式实现。

View File

@ -1,10 +1,10 @@
using GFramework.Core.Abstractions.Rule; using GFramework.Core.Abstractions.Rule;
using GFramework.Core.Coroutine; using GFramework.Core.Coroutine;
using GFramework.Core.Coroutine.Extensions; using GFramework.Core.Coroutine.Extensions;
using GFramework.Core.Extensions;
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs.Command; using GFramework.Cqrs.Abstractions.Cqrs.Command;
using GFramework.Cqrs.Abstractions.Cqrs.Query; using GFramework.Cqrs.Abstractions.Cqrs.Query;
using GFramework.Cqrs.Extensions;
namespace GFramework.Godot.Coroutine; namespace GFramework.Godot.Coroutine;

View File

@ -15,6 +15,11 @@ public static class PathContests
/// </summary> /// </summary>
public const string CoreNamespace = $"{BaseNamespace}.Core"; public const string CoreNamespace = $"{BaseNamespace}.Core";
/// <summary>
/// GFramework CQRS runtime 命名空间
/// </summary>
public const string CqrsNamespace = $"{BaseNamespace}.Cqrs";
/// <summary> /// <summary>
/// GFramework Godot模块命名空间 /// GFramework Godot模块命名空间
/// </summary> /// </summary>
@ -45,4 +50,9 @@ public static class PathContests
/// GFramework核心抽象层命名空间 /// GFramework核心抽象层命名空间
/// </summary> /// </summary>
public const string CoreAbstractionsNamespace = $"{CoreNamespace}.Abstractions"; public const string CoreAbstractionsNamespace = $"{CoreNamespace}.Abstractions";
/// <summary>
/// GFramework CQRS 抽象层命名空间
/// </summary>
public const string CqrsAbstractionsNamespace = $"{CqrsNamespace}.Abstractions";
} }

View File

@ -38,7 +38,7 @@ public class CqrsHandlerRegistryGeneratorTests
} }
} }
namespace GFramework.Core.Abstractions.Cqrs namespace GFramework.Cqrs.Abstractions.Cqrs
{ {
public interface IRequest<TResponse> { } public interface IRequest<TResponse> { }
public interface INotification { } public interface INotification { }
@ -47,7 +47,10 @@ public class CqrsHandlerRegistryGeneratorTests
public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { } public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
public interface INotificationHandler<in TNotification> where TNotification : INotification { } public interface INotificationHandler<in TNotification> where TNotification : INotification { }
public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { } public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
}
namespace GFramework.Cqrs
{
public interface ICqrsHandlerRegistry public interface ICqrsHandlerRegistry
{ {
void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger); void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
@ -62,7 +65,7 @@ public class CqrsHandlerRegistryGeneratorTests
namespace TestApp namespace TestApp
{ {
using GFramework.Core.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
public sealed record PingQuery() : IRequest<string>; public sealed record PingQuery() : IRequest<string>;
public sealed record DomainEvent() : INotification; public sealed record DomainEvent() : INotification;
@ -78,11 +81,11 @@ public class CqrsHandlerRegistryGeneratorTests
// <auto-generated /> // <auto-generated />
#nullable enable #nullable enable
[assembly: global::GFramework.Core.Abstractions.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))] [assembly: global::GFramework.Cqrs.CqrsHandlerRegistryAttribute(typeof(global::GFramework.Generated.Cqrs.__GFrameworkGeneratedCqrsHandlerRegistry))]
namespace GFramework.Generated.Cqrs; namespace GFramework.Generated.Cqrs;
internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Core.Abstractions.Cqrs.ICqrsHandlerRegistry internal sealed class __GFrameworkGeneratedCqrsHandlerRegistry : global::GFramework.Cqrs.ICqrsHandlerRegistry
{ {
public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger) public void Register(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services, global::GFramework.Core.Abstractions.Logging.ILogger logger)
{ {
@ -93,19 +96,19 @@ public class CqrsHandlerRegistryGeneratorTests
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient( global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
services, services,
typeof(global::GFramework.Core.Abstractions.Cqrs.IRequestHandler<global::TestApp.PingQuery, string>), typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<global::TestApp.PingQuery, string>),
typeof(global::TestApp.AlphaQueryHandler)); typeof(global::TestApp.AlphaQueryHandler));
logger.Debug("Registered CQRS handler TestApp.AlphaQueryHandler as GFramework.Core.Abstractions.Cqrs.IRequestHandler<TestApp.PingQuery, string>."); logger.Debug("Registered CQRS handler TestApp.AlphaQueryHandler as GFramework.Cqrs.Abstractions.Cqrs.IRequestHandler<TestApp.PingQuery, string>.");
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient( global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
services, services,
typeof(global::GFramework.Core.Abstractions.Cqrs.IStreamRequestHandler<global::TestApp.NumberStream, int>), typeof(global::GFramework.Cqrs.Abstractions.Cqrs.IStreamRequestHandler<global::TestApp.NumberStream, int>),
typeof(global::TestApp.StreamHandler)); typeof(global::TestApp.StreamHandler));
logger.Debug("Registered CQRS handler TestApp.StreamHandler as GFramework.Core.Abstractions.Cqrs.IStreamRequestHandler<TestApp.NumberStream, int>."); logger.Debug("Registered CQRS handler TestApp.StreamHandler as GFramework.Cqrs.Abstractions.Cqrs.IStreamRequestHandler<TestApp.NumberStream, int>.");
global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient( global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient(
services, services,
typeof(global::GFramework.Core.Abstractions.Cqrs.INotificationHandler<global::TestApp.DomainEvent>), typeof(global::GFramework.Cqrs.Abstractions.Cqrs.INotificationHandler<global::TestApp.DomainEvent>),
typeof(global::TestApp.ZetaNotificationHandler)); typeof(global::TestApp.ZetaNotificationHandler));
logger.Debug("Registered CQRS handler TestApp.ZetaNotificationHandler as GFramework.Core.Abstractions.Cqrs.INotificationHandler<TestApp.DomainEvent>."); logger.Debug("Registered CQRS handler TestApp.ZetaNotificationHandler as GFramework.Cqrs.Abstractions.Cqrs.INotificationHandler<TestApp.DomainEvent>.");
} }
} }
@ -143,7 +146,7 @@ public class CqrsHandlerRegistryGeneratorTests
} }
} }
namespace GFramework.Core.Abstractions.Cqrs namespace GFramework.Cqrs.Abstractions.Cqrs
{ {
public interface IRequest<TResponse> { } public interface IRequest<TResponse> { }
public interface INotification { } public interface INotification { }
@ -152,7 +155,10 @@ public class CqrsHandlerRegistryGeneratorTests
public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { } public interface IRequestHandler<in TRequest, TResponse> where TRequest : IRequest<TResponse> { }
public interface INotificationHandler<in TNotification> where TNotification : INotification { } public interface INotificationHandler<in TNotification> where TNotification : INotification { }
public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { } public interface IStreamRequestHandler<in TRequest, out TResponse> where TRequest : IStreamRequest<TResponse> { }
}
namespace GFramework.Cqrs
{
public interface ICqrsHandlerRegistry public interface ICqrsHandlerRegistry
{ {
void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger); void Register(Microsoft.Extensions.DependencyInjection.IServiceCollection services, GFramework.Core.Abstractions.Logging.ILogger logger);
@ -167,7 +173,7 @@ public class CqrsHandlerRegistryGeneratorTests
namespace TestApp namespace TestApp
{ {
using GFramework.Core.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
public sealed record VisibleRequest() : IRequest<string>; public sealed record VisibleRequest() : IRequest<string>;

View File

@ -8,13 +8,17 @@ namespace GFramework.SourceGenerators.Cqrs;
[Generator] [Generator]
public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
{ {
private const string CqrsNamespace = $"{PathContests.CoreAbstractionsNamespace}.Cqrs"; private const string CqrsContractsNamespace = $"{PathContests.CqrsAbstractionsNamespace}.Cqrs";
private const string CqrsRuntimeNamespace = PathContests.CqrsNamespace;
private const string LoggingNamespace = $"{PathContests.CoreAbstractionsNamespace}.Logging"; private const string LoggingNamespace = $"{PathContests.CoreAbstractionsNamespace}.Logging";
private const string IRequestHandlerMetadataName = $"{CqrsNamespace}.IRequestHandler`2"; private const string IRequestHandlerMetadataName = $"{CqrsContractsNamespace}.IRequestHandler`2";
private const string INotificationHandlerMetadataName = $"{CqrsNamespace}.INotificationHandler`1"; private const string INotificationHandlerMetadataName = $"{CqrsContractsNamespace}.INotificationHandler`1";
private const string IStreamRequestHandlerMetadataName = $"{CqrsNamespace}.IStreamRequestHandler`2"; private const string IStreamRequestHandlerMetadataName = $"{CqrsContractsNamespace}.IStreamRequestHandler`2";
private const string ICqrsHandlerRegistryMetadataName = $"{CqrsNamespace}.ICqrsHandlerRegistry"; private const string ICqrsHandlerRegistryMetadataName = $"{CqrsRuntimeNamespace}.ICqrsHandlerRegistry";
private const string CqrsHandlerRegistryAttributeMetadataName = $"{CqrsNamespace}.CqrsHandlerRegistryAttribute";
private const string CqrsHandlerRegistryAttributeMetadataName =
$"{CqrsRuntimeNamespace}.CqrsHandlerRegistryAttribute";
private const string ILoggerMetadataName = $"{LoggingNamespace}.ILogger"; private const string ILoggerMetadataName = $"{LoggingNamespace}.ILogger";
private const string IServiceCollectionMetadataName = "Microsoft.Extensions.DependencyInjection.IServiceCollection"; private const string IServiceCollectionMetadataName = "Microsoft.Extensions.DependencyInjection.IServiceCollection";
private const string GeneratedNamespace = "GFramework.Generated.Cqrs"; private const string GeneratedNamespace = "GFramework.Generated.Cqrs";
@ -273,7 +277,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
builder.AppendLine("#nullable enable"); builder.AppendLine("#nullable enable");
builder.AppendLine(); builder.AppendLine();
builder.Append("[assembly: global::"); builder.Append("[assembly: global::");
builder.Append(CqrsNamespace); builder.Append(CqrsRuntimeNamespace);
builder.Append(".CqrsHandlerRegistryAttribute(typeof(global::"); builder.Append(".CqrsHandlerRegistryAttribute(typeof(global::");
builder.Append(GeneratedNamespace); builder.Append(GeneratedNamespace);
builder.Append('.'); builder.Append('.');
@ -287,7 +291,7 @@ public sealed class CqrsHandlerRegistryGenerator : IIncrementalGenerator
builder.Append("internal sealed class "); builder.Append("internal sealed class ");
builder.Append(GeneratedTypeName); builder.Append(GeneratedTypeName);
builder.Append(" : global::"); builder.Append(" : global::");
builder.Append(CqrsNamespace); builder.Append(CqrsRuntimeNamespace);
builder.AppendLine(".ICqrsHandlerRegistry"); builder.AppendLine(".ICqrsHandlerRegistry");
builder.AppendLine("{"); builder.AppendLine("{");
builder.Append( builder.Append(

View File

@ -4,10 +4,11 @@ using System.Reflection;
using GFramework.Core.Abstractions.Cqrs; using GFramework.Core.Abstractions.Cqrs;
using GFramework.Core.Abstractions.Ioc; using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging; using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Architectures;
using GFramework.Core.Ioc; using GFramework.Core.Ioc;
using GFramework.Core.Logging; using GFramework.Core.Logging;
using GFramework.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs; using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Command;
namespace GFramework.Tests.Common; namespace GFramework.Tests.Common;
@ -20,7 +21,9 @@ namespace GFramework.Tests.Common;
/// </remarks> /// </remarks>
public static class CqrsTestRuntime public static class CqrsTestRuntime
{ {
private static readonly Type CqrsHandlerRegistrarType = typeof(ArchitectureContext).Assembly private static readonly Assembly CqrsRuntimeAssembly = typeof(CommandBase<,>).Assembly;
private static readonly Type CqrsHandlerRegistrarType = CqrsRuntimeAssembly
.GetType( .GetType(
"GFramework.Core.Cqrs.Internal.CqrsHandlerRegistrar", "GFramework.Core.Cqrs.Internal.CqrsHandlerRegistrar",
throwOnError: true)!; throwOnError: true)!;
@ -40,41 +43,6 @@ public static class CqrsTestRuntime
?? throw new InvalidOperationException( ?? throw new InvalidOperationException(
"Failed to locate CqrsHandlerRegistrar.RegisterHandlers."); "Failed to locate CqrsHandlerRegistrar.RegisterHandlers.");
private static readonly Type CqrsDispatcherType = typeof(ArchitectureContext).Assembly
.GetType(
"GFramework.Core.Cqrs.Internal.CqrsDispatcher",
throwOnError: true)!;
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)!;
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.");
/// <summary> /// <summary>
/// 为裸测试容器补齐默认 CQRS runtime seam。 /// 为裸测试容器补齐默认 CQRS runtime seam。
/// </summary> /// </summary>
@ -92,16 +60,15 @@ public static class CqrsTestRuntime
if (container.Get<ICqrsRuntime>() is null) if (container.Get<ICqrsRuntime>() is null)
{ {
var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger(CqrsDispatcherType.Name); var runtimeLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
var runtime = (ICqrsRuntime)CqrsDispatcherConstructor.Invoke([container, runtimeLogger]); var runtime = CqrsRuntimeFactory.CreateRuntime(container, runtimeLogger);
container.Register<ICqrsRuntime>(runtime); container.Register<ICqrsRuntime>(runtime);
} }
if (container.Get<ICqrsHandlerRegistrar>() is null) if (container.Get<ICqrsHandlerRegistrar>() is null)
{ {
var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger(DefaultCqrsHandlerRegistrarType.Name); var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsHandlerRegistrar");
var registrar = var registrar = CqrsRuntimeFactory.CreateHandlerRegistrar(container, registrarLogger);
(ICqrsHandlerRegistrar)DefaultCqrsHandlerRegistrarConstructor.Invoke([container, registrarLogger]);
container.Register<ICqrsHandlerRegistrar>(registrar); container.Register<ICqrsHandlerRegistrar>(registrar);
} }
} }

View File

@ -10,6 +10,7 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\GFramework.Cqrs.Abstractions\GFramework.Cqrs.Abstractions.csproj"/> <ProjectReference Include="..\GFramework.Cqrs.Abstractions\GFramework.Cqrs.Abstractions.csproj"/>
<ProjectReference Include="..\GFramework.Cqrs\GFramework.Cqrs.csproj"/>
<ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj"/> <ProjectReference Include="..\GFramework.Core.Abstractions\GFramework.Core.Abstractions.csproj"/>
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/> <ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
</ItemGroup> </ItemGroup>