// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System.Collections.Concurrent;
using GFramework.Core.Abstractions.Architectures;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Rule;
using GFramework.Cqrs.Abstractions.Cqrs;
using GFramework.Cqrs.Notification;
using ICqrsRuntime = GFramework.Core.Abstractions.Cqrs.ICqrsRuntime;
namespace GFramework.Cqrs.Internal;
///
/// GFramework 自有 CQRS 运行时分发器。
/// 该类型负责解析请求/通知处理器,并在调用前为上下文感知对象注入当前 CQRS 分发上下文。
///
internal sealed class CqrsDispatcher(
IIocContainer container,
ILogger logger,
INotificationPublisher? notificationPublisher) : ICqrsRuntime
{
// 实例级热路径缓存:默认 runtime 在容器冻结前创建,但请求/stream 行为注册在架构生命周期内保持稳定。
// 因此这里按 behavior service type 记住“当前 dispatcher 对应容器里是否存在该行为”,避免 0-pipeline steady-state
// 每次 SendAsync 都重复询问容器。缓存值只反映当前 dispatcher 持有容器的注册可见性,不跨 runtime 共享。
private readonly ConcurrentDictionary _requestBehaviorPresenceCache = new();
// 与 request 路径相同,stream 的 behavior 注册可见性在当前 dispatcher 生命周期内保持稳定。
// 这里缓存 “CreateStream(...) 对应 behaviorType 是否存在注册”,避免零管道 stream 每次建流都重复询问容器。
private readonly ConcurrentDictionary _streamBehaviorPresenceCache = new();
// 卸载安全的进程级缓存:当 generated registry 提供 request invoker 元数据时,
// registrar 会按请求/响应类型对把它们写入这里;若类型被卸载,条目会自然失效。
private static readonly WeakTypePairCache
GeneratedRequestInvokers = new();
// 卸载安全的进程级缓存:当 generated registry 提供 stream invoker 元数据时,
// registrar 会按流式请求/响应类型对把它们写入这里;若类型被卸载,条目会自然失效。
private static readonly WeakTypePairCache
GeneratedStreamInvokers = new();
// 卸载安全的进程级缓存:通知类型只以弱键语义保留。
// 若插件/热重载程序集中的通知类型被卸载,对应分发绑定会自然失效,下次命中时再重新计算。
private static readonly WeakKeyCache
NotificationDispatchBindings = new();
// 卸载安全的进程级缓存:流式请求/响应类型对命中后复用强类型 dispatch binding 盒子,
// 避免 stream 响应元素在热路径上退化为 object 桥接,同时仍保持弱键卸载安全语义。
private static readonly WeakTypePairCache
StreamDispatchBindings = new();
// 卸载安全的进程级缓存:请求/响应类型对命中后复用强类型 dispatch binding;
// 若任一类型被回收,后续首次发送时会按当前加载状态重新生成。
private static readonly WeakTypePairCache
RequestDispatchBindings = new();
// 静态方法定义缓存:这些反射查找与消息类型无关,只需解析一次即可复用。
private static readonly MethodInfo RequestHandlerInvokerMethodDefinition = typeof(CqrsDispatcher)
.GetMethod(nameof(InvokeRequestHandlerAsync), BindingFlags.NonPublic | BindingFlags.Static)!;
private static readonly MethodInfo RequestPipelineInvokerMethodDefinition = typeof(CqrsDispatcher)
.GetMethod(nameof(InvokeRequestPipelineExecutorAsync), BindingFlags.NonPublic | BindingFlags.Static)!;
private static readonly MethodInfo NotificationHandlerInvokerMethodDefinition = typeof(CqrsDispatcher)
.GetMethod(nameof(InvokeNotificationHandlerAsync), BindingFlags.NonPublic | BindingFlags.Static)!;
private static readonly MethodInfo StreamHandlerInvokerMethodDefinition = typeof(CqrsDispatcher)
.GetMethod(nameof(InvokeStreamHandler), BindingFlags.NonPublic | BindingFlags.Static)!;
private static readonly MethodInfo StreamPipelineInvokerMethodDefinition = typeof(CqrsDispatcher)
.GetMethod(nameof(InvokeStreamPipelineExecutor), BindingFlags.NonPublic | BindingFlags.Static)!;
// runtime 通常会在容器冻结前创建;此时通过实现类型注册的 notification publisher
// 还没有被底层 provider 物化,因此不能只在构造阶段抓取一次。
// 显式传入实例时仍优先复用该实例;否则在真正 publish 时再尝试从容器解析。
private readonly INotificationPublisher? _notificationPublisher = notificationPublisher;
// 容器冻结后 notification publisher 解析结果在当前 dispatcher 生命周期内保持稳定;
// 因此首次 publish 后缓存最终策略实例,避免后续热路径重复查容器和重复分配默认 publisher。
private INotificationPublisher? _resolvedNotificationPublisher;
///
/// 发布通知到所有已注册处理器。
///
/// 通知类型。
/// 当前 CQRS 分发上下文,用于上下文感知处理器注入。
/// 通知对象。
/// 取消令牌。
public async ValueTask PublishAsync(
ICqrsContext context,
TNotification notification,
CancellationToken cancellationToken = default)
where TNotification : INotification
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(notification);
var notificationType = notification.GetType();
var dispatchBinding = NotificationDispatchBindings.GetOrAdd(
notificationType,
static notificationType => CreateNotificationDispatchBinding(notificationType));
var handlers = container.GetAll(dispatchBinding.HandlerType);
if (handlers.Count == 0)
{
logger.Debug($"No CQRS notification handler registered for {notificationType.FullName}.");
return;
}
var publishContext = CreateNotificationPublishContext(notification, handlers, context, dispatchBinding.Invoker);
await ResolveNotificationPublisher().PublishAsync(publishContext, cancellationToken).ConfigureAwait(false);
}
///
/// 发送请求并返回结果。
///
/// 响应类型。
/// 当前 CQRS 分发上下文,用于上下文感知处理器注入。
/// 请求对象。
/// 取消令牌。
/// 请求响应。
public ValueTask SendAsync(
ICqrsContext context,
IRequest request,
CancellationToken cancellationToken = default)
{
try
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(request);
var requestType = request.GetType();
var dispatchBinding = GetRequestDispatchBinding(requestType);
var handler = container.Get(dispatchBinding.HandlerType)
?? throw new InvalidOperationException(
$"No CQRS request handler registered for {requestType.FullName}.");
PrepareHandler(handler, context);
if (!HasRequestBehaviorRegistration(dispatchBinding.BehaviorType))
{
return dispatchBinding.RequestInvoker(handler, request, cancellationToken);
}
var behaviors = container.GetAll(dispatchBinding.BehaviorType);
foreach (var behavior in behaviors)
{
PrepareHandler(behavior, context);
}
return dispatchBinding.GetPipelineExecutor(behaviors.Count)
.Invoke(handler, behaviors, request, cancellationToken);
}
catch (Exception exception)
{
// 保留旧 async 实现的 faulted-ValueTask 失败语义,同时继续复用 direct-return 的热路径。
return ValueTask.FromException(exception);
}
}
///
/// 读取当前 dispatcher 容器里是否存在指定 request pipeline 行为注册,并在首次命中后缓存结果。
///
/// 目标 pipeline 行为服务类型。
/// 存在注册时返回 ;否则返回 。
private bool HasRequestBehaviorRegistration(Type behaviorType)
{
ArgumentNullException.ThrowIfNull(behaviorType);
return _requestBehaviorPresenceCache.GetOrAdd(
behaviorType,
static (cachedBehaviorType, currentContainer) => currentContainer.HasRegistration(cachedBehaviorType),
container);
}
///
/// 创建流式请求并返回异步响应序列。
///
/// 响应元素类型。
/// 当前 CQRS 分发上下文,用于上下文感知处理器注入。
/// 流式请求对象。
/// 取消令牌。
/// 异步响应序列。
public IAsyncEnumerable CreateStream(
ICqrsContext context,
IStreamRequest request,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(context);
ArgumentNullException.ThrowIfNull(request);
var requestType = request.GetType();
var dispatchBinding = GetStreamDispatchBinding(requestType);
var handler = container.Get(dispatchBinding.HandlerType)
?? throw new InvalidOperationException(
$"No CQRS stream handler registered for {requestType.FullName}.");
PrepareHandler(handler, context);
if (!HasStreamBehaviorRegistration(dispatchBinding.BehaviorType))
{
return dispatchBinding.StreamInvoker(handler, request, cancellationToken);
}
var behaviors = container.GetAll(dispatchBinding.BehaviorType);
foreach (var behavior in behaviors)
{
PrepareHandler(behavior, context);
}
return dispatchBinding.GetPipelineExecutor(behaviors.Count)
.Invoke(handler, behaviors, dispatchBinding.StreamInvoker, request, cancellationToken);
}
///
/// 读取当前 dispatcher 容器里是否存在指定 stream pipeline 行为注册,并在首次命中后缓存结果。
///
/// 目标 stream pipeline 行为服务类型。
/// 存在注册时返回 ;否则返回 。
private bool HasStreamBehaviorRegistration(Type behaviorType)
{
ArgumentNullException.ThrowIfNull(behaviorType);
return _streamBehaviorPresenceCache.GetOrAdd(
behaviorType,
static (cachedBehaviorType, currentContainer) => currentContainer.HasRegistration(cachedBehaviorType),
container);
}
///
/// 为上下文感知处理器注入当前 CQRS 分发上下文。
///
/// 处理器实例。
/// 当前 CQRS 分发上下文。
private static void PrepareHandler(object handler, ICqrsContext context)
{
if (handler is IContextAware contextAware)
{
if (context is not IArchitectureContext architectureContext)
throw new InvalidOperationException(
"The current CQRS context does not implement IArchitectureContext, so it cannot be injected into IContextAware handlers.");
contextAware.SetContext(architectureContext);
}
}
///
/// 解析当前 publish 调用应使用的 notification publisher。
///
///
/// 显式传入实例的路径优先;若调用方只在组合根里声明了 类型映射,
/// 则在容器冻结后的首次 publish 才能拿到底层 provider 构造出来的实例。
/// 若容器中仍未声明任何策略,则回退到默认顺序发布器。
///
private INotificationPublisher ResolveNotificationPublisher()
{
if (_notificationPublisher is not null)
{
return _notificationPublisher;
}
var resolvedNotificationPublisher = _resolvedNotificationPublisher;
if (resolvedNotificationPublisher is not null)
{
return resolvedNotificationPublisher;
}
var registeredPublishers = container.GetAll(typeof(INotificationPublisher));
resolvedNotificationPublisher = registeredPublishers.Count switch
{
0 => new SequentialNotificationPublisher(),
1 => (INotificationPublisher)registeredPublishers[0],
_ => throw new InvalidOperationException(
$"Multiple {typeof(INotificationPublisher).FullName} instances are registered. Remove duplicate notification publisher strategies before publishing notifications.")
};
Interlocked.CompareExchange(
ref _resolvedNotificationPublisher,
resolvedNotificationPublisher,
comparand: null);
return _resolvedNotificationPublisher;
}
///
/// 为指定请求类型构造完整分发绑定,把服务类型与强类型调用委托一次性收敛到同一缓存项。
///
private static RequestDispatchBinding CreateRequestDispatchBinding(Type requestType)
{
var generatedDescriptor = TryGetGeneratedRequestInvokerDescriptor(requestType);
if (generatedDescriptor is not null)
{
var resolvedGeneratedDescriptor = generatedDescriptor.Value;
return new RequestDispatchBinding(
resolvedGeneratedDescriptor.HandlerType,
typeof(IPipelineBehavior<,>).MakeGenericType(requestType, typeof(TResponse)),
resolvedGeneratedDescriptor.Invoker,
requestType);
}
return new RequestDispatchBinding(
typeof(IRequestHandler<,>).MakeGenericType(requestType, typeof(TResponse)),
typeof(IPipelineBehavior<,>).MakeGenericType(requestType, typeof(TResponse)),
CreateRequestInvoker(requestType),
requestType);
}
///
/// 获取指定请求/响应类型对的 dispatch binding;若缓存未命中则按当前加载状态创建。
///
private static RequestDispatchBinding GetRequestDispatchBinding(Type requestType)
{
var bindingBox = RequestDispatchBindings.GetOrAdd(
requestType,
typeof(TResponse),
static (cachedRequestType, cachedResponseType) =>
CreateRequestDispatchBindingBox(cachedRequestType, cachedResponseType));
return bindingBox.Get();
}
///
/// 为弱键请求缓存创建强类型 binding 盒子,避免 value-type 响应走 object 结果桥接。
///
private static RequestDispatchBindingBox CreateRequestDispatchBindingBox(
Type requestType,
Type responseType)
{
if (responseType != typeof(TResponse))
throw new InvalidOperationException(
$"Request dispatch binding cache expected response type {typeof(TResponse).FullName}, but received {responseType.FullName}.");
return RequestDispatchBindingBox.Create(CreateRequestDispatchBinding(requestType));
}
///
/// 尝试从容器已注册的 generated request invoker provider 中获取指定请求/响应类型对的元数据。
///
/// 当前请求响应类型。
/// 请求运行时类型。
/// 命中时返回强类型化后的描述符;否则返回 。
private static RequestInvokerDescriptor? TryGetGeneratedRequestInvokerDescriptor(Type requestType)
{
return GeneratedRequestInvokers.TryGetValue(requestType, typeof(TResponse), out var metadata) &&
metadata is not null
? CreateRequestInvokerDescriptor(requestType, metadata)
: null;
}
///
/// 把 provider 返回的弱类型描述符转换为 dispatcher 内部使用的强类型 request invoker 描述符。
///
/// 当前请求响应类型。
/// 请求运行时类型。
/// provider 返回的弱类型描述符。
/// 可直接用于创建 request dispatch binding 的强类型描述符。
/// 当 provider 返回的委托签名与当前请求/响应类型对不匹配时抛出。
private static RequestInvokerDescriptor CreateRequestInvokerDescriptor(
Type requestType,
GeneratedRequestInvokerMetadata descriptor)
{
if (!descriptor.InvokerMethod.IsStatic)
{
throw new InvalidOperationException(
$"Generated CQRS request invoker provider returned a non-static invoker method for request type {requestType.FullName} and response type {typeof(TResponse).FullName}.");
}
try
{
if (Delegate.CreateDelegate(typeof(RequestInvoker), descriptor.InvokerMethod) is not
RequestInvoker invoker)
{
throw new InvalidOperationException(
$"Generated CQRS request invoker provider returned an incompatible invoker for request type {requestType.FullName} and response type {typeof(TResponse).FullName}.");
}
return new RequestInvokerDescriptor(descriptor.HandlerType, invoker);
}
catch (ArgumentException exception)
{
throw new InvalidOperationException(
$"Generated CQRS request invoker provider returned an incompatible invoker for request type {requestType.FullName} and response type {typeof(TResponse).FullName}.",
exception);
}
}
///
/// 为指定通知类型构造完整分发绑定,把服务类型与调用委托聚合到同一缓存项。
///
private static NotificationDispatchBinding CreateNotificationDispatchBinding(Type notificationType)
{
return new NotificationDispatchBinding(
typeof(INotificationHandler<>).MakeGenericType(notificationType),
CreateNotificationInvoker(notificationType));
}
///
/// 为指定流式请求类型构造完整分发绑定,把服务类型与调用委托聚合到同一缓存项。
///
private static StreamDispatchBinding CreateStreamDispatchBinding(Type requestType)
{
var generatedDescriptor = TryGetGeneratedStreamInvokerDescriptor(requestType);
if (generatedDescriptor is not null)
{
var resolvedGeneratedDescriptor = generatedDescriptor.Value;
return new StreamDispatchBinding(
resolvedGeneratedDescriptor.HandlerType,
typeof(IStreamPipelineBehavior<,>).MakeGenericType(requestType, typeof(TResponse)),
requestType,
resolvedGeneratedDescriptor.Invoker);
}
return new StreamDispatchBinding(
typeof(IStreamRequestHandler<,>).MakeGenericType(requestType, typeof(TResponse)),
typeof(IStreamPipelineBehavior<,>).MakeGenericType(requestType, typeof(TResponse)),
requestType,
CreateStreamInvoker(requestType));
}
///
/// 获取指定流式请求/响应类型对的 dispatch binding;若缓存未命中则按当前加载状态创建。
///
/// 流式响应元素类型。
/// 流式请求运行时类型。
/// 当前请求/响应类型对对应的强类型 stream dispatch binding。
private static StreamDispatchBinding GetStreamDispatchBinding(Type requestType)
{
var bindingBox = StreamDispatchBindings.GetOrAdd(
requestType,
typeof(TResponse),
static (cachedRequestType, cachedResponseType) =>
CreateStreamDispatchBindingBox(cachedRequestType, cachedResponseType));
return bindingBox.Get();
}
///
/// 为弱键流式请求缓存创建强类型 binding 盒子,避免响应元素走 object 结果桥接。
///
/// 流式响应元素类型。
/// 流式请求运行时类型。
/// 缓存命中的响应运行时类型。
/// 可放入弱键缓存的强类型 binding 盒子。
private static StreamDispatchBindingBox CreateStreamDispatchBindingBox(
Type requestType,
Type responseType)
{
if (responseType != typeof(TResponse))
{
throw new InvalidOperationException(
$"Stream dispatch binding cache expected response type {typeof(TResponse).FullName}, but received {responseType.FullName}.");
}
return StreamDispatchBindingBox.Create(CreateStreamDispatchBinding(requestType));
}
///
/// 尝试从容器已注册的 generated stream invoker provider 中获取指定流式请求/响应类型对的元数据。
///
/// 流式响应元素类型。
/// 流式请求运行时类型。
/// 命中时返回强类型化后的描述符;否则返回 。
private static StreamInvokerDescriptor? TryGetGeneratedStreamInvokerDescriptor(Type requestType)
{
return GeneratedStreamInvokers.TryGetValue(requestType, typeof(TResponse), out var metadata) &&
metadata is not null
? CreateStreamInvokerDescriptor(requestType, metadata)
: null;
}
///
/// 把 provider 返回的弱类型描述符转换为 dispatcher 内部使用的 stream invoker 描述符。
///
/// 流式响应元素类型。
/// 流式请求运行时类型。
/// provider 返回的弱类型描述符。
/// 可直接用于创建 stream dispatch binding 的描述符。
/// 当 provider 返回的委托签名与当前流式请求/响应类型对不匹配时抛出。
private static StreamInvokerDescriptor CreateStreamInvokerDescriptor(
Type requestType,
GeneratedStreamInvokerMetadata descriptor)
{
if (!descriptor.InvokerMethod.IsStatic)
{
throw new InvalidOperationException(
$"Generated CQRS stream invoker provider returned a non-static invoker method for request type {requestType.FullName} and response type {typeof(TResponse).FullName}.");
}
try
{
if (Delegate.CreateDelegate(typeof(WeakStreamInvoker), descriptor.InvokerMethod) is not
WeakStreamInvoker weakInvoker)
{
throw new InvalidOperationException(
$"Generated CQRS stream invoker provider returned an incompatible invoker for request type {requestType.FullName} and response type {typeof(TResponse).FullName}.");
}
// generated stream descriptor 的公开契约仍以 object 返回值暴露异步流;
// 这里在 binding 创建时只做一次适配,把后续 CreateStream 热路径保持为强类型调用。
var adapter = new GeneratedStreamInvokerAdapter(weakInvoker);
StreamInvoker invoker = (handler, request, cancellationToken) =>
adapter.Invoke(handler, request, cancellationToken);
return new StreamInvokerDescriptor(descriptor.HandlerType, invoker);
}
catch (ArgumentException exception)
{
throw new InvalidOperationException(
$"Generated CQRS stream invoker provider returned an incompatible invoker for request type {requestType.FullName} and response type {typeof(TResponse).FullName}.",
exception);
}
}
///
/// 生成请求处理器调用委托,避免每次发送都重复反射。
///
private static RequestInvoker CreateRequestInvoker(Type requestType)
{
var method = RequestHandlerInvokerMethodDefinition
.MakeGenericMethod(requestType, typeof(TResponse));
return (RequestInvoker)Delegate.CreateDelegate(typeof(RequestInvoker), method);
}
///
/// 生成通知处理器调用委托,避免每次发布都重复反射。
///
private static NotificationInvoker CreateNotificationInvoker(Type notificationType)
{
var method = NotificationHandlerInvokerMethodDefinition
.MakeGenericMethod(notificationType);
return (NotificationInvoker)Delegate.CreateDelegate(typeof(NotificationInvoker), method);
}
///
/// 为当前通知发布调用创建发布上下文,把处理器集合与执行入口收敛到同一对象。
///
/// 通知类型。
/// 当前通知。
/// 当前发布调用已解析到的处理器集合。
/// 当前 CQRS 分发上下文。
/// 执行单个通知处理器时复用的强类型调用委托。
/// 供通知发布器消费的执行上下文。
private static NotificationPublishContext CreateNotificationPublishContext(
TNotification notification,
IReadOnlyList