mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-07 00:39:00 +08:00
feat(ioc): 添加 Microsoft DI 容器适配器和 CQRS 运行时模块
- 实现 MicrosoftDiContainer 类,提供对 Microsoft.Extensions.DependencyInjection 的适配 - 添加线程安全的依赖注入容器功能,支持单例、瞬态和作用域服务注册 - 实现 CqrsRuntimeModule 模块,用于注册 CQRS 运行时组件 - 添加 CqrsRuntimeFactory 工厂类,提供 CQRS 运行时实现的创建入口 - 实现 DefaultCqrsRegistrationService,处理 CQRS 处理器的程序集注册 - 添加 CqrsTestRuntime 测试工具类,为测试环境提供 CQRS 运行时访问 - 支持多种注册方式包括实例注册、类型映射和工厂方法 - 实现服务获取、查询和生命周期管理功能 - 添加容器冻结机制以构建服务提供者 - 支持 CQRS 管道行为和处理器的批量注册功能
This commit is contained in:
parent
1973fb2a60
commit
a7604de804
@ -5,6 +5,7 @@ using GFramework.Core.Abstractions.Ioc;
|
|||||||
using GFramework.Core.Abstractions.Logging;
|
using GFramework.Core.Abstractions.Logging;
|
||||||
using GFramework.Core.Abstractions.Systems;
|
using GFramework.Core.Abstractions.Systems;
|
||||||
using GFramework.Core.Rule;
|
using GFramework.Core.Rule;
|
||||||
|
using GFramework.Cqrs;
|
||||||
using GFramework.Cqrs.Abstractions.Cqrs;
|
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||||
|
|
||||||
namespace GFramework.Core.Ioc;
|
namespace GFramework.Core.Ioc;
|
||||||
@ -56,12 +57,6 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly HashSet<object> _registeredInstances = [];
|
private readonly HashSet<object> _registeredInstances = [];
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 已接入 CQRS handler 注册流程的程序集键集合。
|
|
||||||
/// 使用稳定字符串键而不是 Assembly 引用本身,以避免默认路径和显式扩展路径使用不同 Assembly 对象时重复注册。
|
|
||||||
/// </summary>
|
|
||||||
private readonly HashSet<string> _registeredCqrsHandlerAssemblyKeys = new(StringComparer.Ordinal);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 日志记录器,用于记录容器操作日志
|
/// 日志记录器,用于记录容器操作日志
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -405,26 +400,7 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
ThrowIfFrozen();
|
ThrowIfFrozen();
|
||||||
|
ResolveCqrsRegistrationService().RegisterHandlers(assemblies);
|
||||||
var processedAssemblyKeys = new HashSet<string>(StringComparer.Ordinal);
|
|
||||||
foreach (var assembly in assemblies
|
|
||||||
.Where(static assembly => assembly is not null)
|
|
||||||
.OrderBy(GetCqrsAssemblyRegistrationKey, StringComparer.Ordinal))
|
|
||||||
{
|
|
||||||
var assemblyKey = GetCqrsAssemblyRegistrationKey(assembly);
|
|
||||||
if (!processedAssemblyKeys.Add(assemblyKey))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (_registeredCqrsHandlerAssemblyKeys.Contains(assemblyKey))
|
|
||||||
{
|
|
||||||
_logger.Debug(
|
|
||||||
$"Skipping CQRS handler registration for assembly {assemblyKey} because it was already registered.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ResolveCqrsHandlerRegistrar().RegisterHandlers([assembly]);
|
|
||||||
_registeredCqrsHandlerAssemblyKeys.Add(assemblyKey);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@ -455,22 +431,22 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
#region Get
|
#region Get
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取当前容器中已注册的 CQRS 处理器注册器。
|
/// 获取当前容器中已注册的 CQRS 程序集注册协调器。
|
||||||
/// 该方法仅供容器内部在注册阶段使用,因此直接读取服务描述符中的实例绑定,
|
/// 该方法仅供容器内部在注册阶段使用,因此直接读取服务描述符中的实例绑定,
|
||||||
/// 避免在容器未冻结前依赖完整的服务提供者构建流程。
|
/// 避免在容器未冻结前依赖完整的服务提供者构建流程。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>已注册的 CQRS 处理器注册器实例。</returns>
|
/// <returns>已注册的 CQRS 程序集注册协调器实例。</returns>
|
||||||
/// <exception cref="InvalidOperationException">未找到可用的 CQRS 处理器注册器实例时抛出。</exception>
|
/// <exception cref="InvalidOperationException">未找到可用的 CQRS 程序集注册协调器实例时抛出。</exception>
|
||||||
private ICqrsHandlerRegistrar ResolveCqrsHandlerRegistrar()
|
private ICqrsRegistrationService ResolveCqrsRegistrationService()
|
||||||
{
|
{
|
||||||
var descriptor = GetServicesUnsafe.LastOrDefault(static service =>
|
var descriptor = GetServicesUnsafe.LastOrDefault(static service =>
|
||||||
service.ServiceType == typeof(ICqrsHandlerRegistrar));
|
service.ServiceType == typeof(ICqrsRegistrationService));
|
||||||
|
|
||||||
if (descriptor?.ImplementationInstance is ICqrsHandlerRegistrar registrar)
|
if (descriptor?.ImplementationInstance is ICqrsRegistrationService registrationService)
|
||||||
return registrar;
|
return registrationService;
|
||||||
|
|
||||||
const string errorMessage =
|
const string errorMessage =
|
||||||
"ICqrsHandlerRegistrar not registered. Ensure the CQRS runtime module has been installed before registering handlers.";
|
"ICqrsRegistrationService not registered. Ensure the CQRS runtime module has been installed before registering handlers.";
|
||||||
_logger.Error(errorMessage);
|
_logger.Error(errorMessage);
|
||||||
throw new InvalidOperationException(errorMessage);
|
throw new InvalidOperationException(errorMessage);
|
||||||
}
|
}
|
||||||
@ -832,7 +808,6 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
|
|
||||||
GetServicesUnsafe.Clear();
|
GetServicesUnsafe.Clear();
|
||||||
_registeredInstances.Clear();
|
_registeredInstances.Clear();
|
||||||
_registeredCqrsHandlerAssemblyKeys.Clear();
|
|
||||||
_provider = null;
|
_provider = null;
|
||||||
_logger.Info("Container cleared");
|
_logger.Info("Container cleared");
|
||||||
}
|
}
|
||||||
@ -904,16 +879,5 @@ public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 生成 CQRS handler 注册用的稳定程序集键。
|
|
||||||
/// 该键需要同时兼顾真实程序集与测试中使用的 mocked Assembly,避免仅靠引用比较导致重复接入。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="assembly">目标程序集。</param>
|
|
||||||
/// <returns>稳定的程序集标识字符串。</returns>
|
|
||||||
private static string GetCqrsAssemblyRegistrationKey(Assembly assembly)
|
|
||||||
{
|
|
||||||
return assembly.FullName ?? assembly.GetName().Name ?? assembly.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,12 +39,15 @@ public sealed class CqrsRuntimeModule : IServiceModule
|
|||||||
|
|
||||||
var dispatcherLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
|
var dispatcherLogger = LoggerFactoryResolver.Provider.CreateLogger("CqrsDispatcher");
|
||||||
var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsHandlerRegistrar");
|
var registrarLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsHandlerRegistrar");
|
||||||
|
var registrationLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsRegistrationService");
|
||||||
var runtime = CqrsRuntimeFactory.CreateRuntime(container, dispatcherLogger);
|
var runtime = CqrsRuntimeFactory.CreateRuntime(container, dispatcherLogger);
|
||||||
|
var registrar = CqrsRuntimeFactory.CreateHandlerRegistrar(container, registrarLogger);
|
||||||
|
|
||||||
container.Register(runtime);
|
container.Register(runtime);
|
||||||
container.Register<LegacyICqrsRuntime>((LegacyICqrsRuntime)runtime);
|
container.Register<LegacyICqrsRuntime>((LegacyICqrsRuntime)runtime);
|
||||||
container.Register<ICqrsHandlerRegistrar>(
|
container.Register<ICqrsHandlerRegistrar>(registrar);
|
||||||
CqrsRuntimeFactory.CreateHandlerRegistrar(container, registrarLogger));
|
container.Register<ICqrsRegistrationService>(
|
||||||
|
CqrsRuntimeFactory.CreateRegistrationService(registrar, registrationLogger));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -47,4 +47,21 @@ public static class CqrsRuntimeFactory
|
|||||||
|
|
||||||
return new DefaultCqrsHandlerRegistrar(container, logger);
|
return new DefaultCqrsHandlerRegistrar(container, logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建默认的 CQRS 程序集注册协调器。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="registrar">底层 handler 注册器。</param>
|
||||||
|
/// <param name="logger">用于注册阶段诊断的日志器。</param>
|
||||||
|
/// <returns>默认 CQRS 程序集注册协调器。</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">
|
||||||
|
/// <paramref name="registrar" /> 或 <paramref name="logger" /> 为 <see langword="null" />。
|
||||||
|
/// </exception>
|
||||||
|
public static ICqrsRegistrationService CreateRegistrationService(ICqrsHandlerRegistrar registrar, ILogger logger)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(registrar);
|
||||||
|
ArgumentNullException.ThrowIfNull(logger);
|
||||||
|
|
||||||
|
return new DefaultCqrsRegistrationService(registrar, logger);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
GFramework.Cqrs/ICqrsRegistrationService.cs
Normal file
19
GFramework.Cqrs/ICqrsRegistrationService.cs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace GFramework.Cqrs;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 协调 CQRS 处理器程序集的接入流程。
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 该服务封装“程序集去重 + 生成注册器优先 + 反射回退”的默认接入语义,
|
||||||
|
/// 让 <c>GFramework.Core</c> 的容器层只保留公开入口,而不再直接维护 CQRS handler 注册细节。
|
||||||
|
/// </remarks>
|
||||||
|
public interface ICqrsRegistrationService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 注册一个或多个程序集中的 CQRS 处理器。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assemblies">要接入的程序集集合。</param>
|
||||||
|
void RegisterHandlers(IEnumerable<Assembly> assemblies);
|
||||||
|
}
|
||||||
59
GFramework.Cqrs/Internal/DefaultCqrsRegistrationService.cs
Normal file
59
GFramework.Cqrs/Internal/DefaultCqrsRegistrationService.cs
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using GFramework.Core.Abstractions.Logging;
|
||||||
|
using GFramework.Cqrs.Abstractions.Cqrs;
|
||||||
|
|
||||||
|
namespace GFramework.Cqrs.Internal;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 默认的 CQRS 程序集注册协调器。
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// 该实现把“按稳定程序集键去重”和“委托给 handler registrar 执行实际映射注册”收敛到 CQRS runtime 内部,
|
||||||
|
/// 避免外层容器继续了解 handler 注册流水线的内部结构。
|
||||||
|
/// </remarks>
|
||||||
|
internal sealed class DefaultCqrsRegistrationService(ICqrsHandlerRegistrar registrar, ILogger logger)
|
||||||
|
: ICqrsRegistrationService
|
||||||
|
{
|
||||||
|
private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
private readonly HashSet<string> _registeredAssemblyKeys = new(StringComparer.Ordinal);
|
||||||
|
private readonly ICqrsHandlerRegistrar _registrar = registrar ?? throw new ArgumentNullException(nameof(registrar));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册指定程序集中的 CQRS handlers。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assemblies">要接入的程序集集合。</param>
|
||||||
|
public void RegisterHandlers(IEnumerable<Assembly> assemblies)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(assemblies);
|
||||||
|
|
||||||
|
var processedAssemblyKeys = new HashSet<string>(StringComparer.Ordinal);
|
||||||
|
foreach (var assembly in assemblies
|
||||||
|
.Where(static assembly => assembly is not null)
|
||||||
|
.OrderBy(GetAssemblyRegistrationKey, StringComparer.Ordinal))
|
||||||
|
{
|
||||||
|
var assemblyKey = GetAssemblyRegistrationKey(assembly);
|
||||||
|
if (!processedAssemblyKeys.Add(assemblyKey))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (_registeredAssemblyKeys.Contains(assemblyKey))
|
||||||
|
{
|
||||||
|
_logger.Debug(
|
||||||
|
$"Skipping CQRS handler registration for assembly {assemblyKey} because it was already registered.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_registrar.RegisterHandlers([assembly]);
|
||||||
|
_registeredAssemblyKeys.Add(assemblyKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 生成稳定程序集键,避免相同程序集被不同 <see cref="Assembly" /> 实例重复接入。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="assembly">目标程序集。</param>
|
||||||
|
/// <returns>稳定的程序集标识。</returns>
|
||||||
|
private static string GetAssemblyRegistrationKey(Assembly assembly)
|
||||||
|
{
|
||||||
|
return assembly.FullName ?? assembly.GetName().Name ?? assembly.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -74,6 +74,14 @@ public static class CqrsTestRuntime
|
|||||||
var registrar = CqrsRuntimeFactory.CreateHandlerRegistrar(container, registrarLogger);
|
var registrar = CqrsRuntimeFactory.CreateHandlerRegistrar(container, registrarLogger);
|
||||||
container.Register<ICqrsHandlerRegistrar>(registrar);
|
container.Register<ICqrsHandlerRegistrar>(registrar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (container.Get<ICqrsRegistrationService>() is null)
|
||||||
|
{
|
||||||
|
var registrationLogger = LoggerFactoryResolver.Provider.CreateLogger("DefaultCqrsRegistrationService");
|
||||||
|
var registrar = container.GetRequired<ICqrsHandlerRegistrar>();
|
||||||
|
var registrationService = CqrsRuntimeFactory.CreateRegistrationService(registrar, registrationLogger);
|
||||||
|
container.Register<ICqrsRegistrationService>(registrationService);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user