GFramework/GFramework.Core/Ioc/MicrosoftDiContainer.cs
gewuyou 120a1487f5 perf(cqrs): 收口请求热路径常量开销
- 优化 CqrsDispatcher.SendAsync 的 direct-return ValueTask 路径,移除 dispatcher 自身的异步状态机开销

- 引入 MicrosoftDiContainer 冻结后服务键索引,收敛 HasRegistration(Type) 的重复描述符扫描

- 更新 cqrs-rewrite active tracking 与 trace,记录 RP-104 的基线、验证结果与下一批建议
2026-05-08 11:38:27 +08:00

1337 lines
47 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 2025-2026 GeWuYou
// SPDX-License-Identifier: Apache-2.0
using System.Reflection;
using System.Threading;
using GFramework.Core.Abstractions.Bases;
using GFramework.Core.Abstractions.Ioc;
using GFramework.Core.Abstractions.Logging;
using GFramework.Core.Abstractions.Systems;
using GFramework.Core.Rule;
using GFramework.Cqrs;
using GFramework.Cqrs.Abstractions.Cqrs;
namespace GFramework.Core.Ioc;
/// <summary>
/// Microsoft.Extensions.DependencyInjection 适配器
/// 将 Microsoft DI 包装为 IIocContainer 接口实现
/// 提供线程安全的依赖注入容器功能
/// </summary>
/// <remarks>
/// 该适配器负责维护服务注册表、冻结后的根 <see cref="IServiceProvider" /> 以及并发访问控制。
/// 容器释放后会阻止任何进一步访问,并统一抛出 <see cref="ObjectDisposedException" />
/// 以避免 benchmark、测试宿主或短生命周期架构误用失效的 DI 状态。
/// </remarks>
/// <param name="serviceCollection">可选的IServiceCollection实例默认创建新的ServiceCollection</param>
public class MicrosoftDiContainer(IServiceCollection? serviceCollection = null) : ContextAwareBase, IIocContainer
{
#region Helper Methods
/// <summary>
/// 抛出统一的容器释放异常,避免并发路径泄露底层锁类型的实现细节。
/// </summary>
/// <exception cref="ObjectDisposedException">始终抛出,且对象名固定为当前容器类型。</exception>
private void ThrowDisposedException()
{
const string objectName = nameof(MicrosoftDiContainer);
_logger.Warn("Attempted to use a disposed MicrosoftDiContainer.");
throw new ObjectDisposedException(objectName);
}
/// <summary>
/// 检查容器是否已释放,避免访问已经失效的服务提供者与同步原语。
/// </summary>
/// <exception cref="ObjectDisposedException">当容器已释放时抛出。</exception>
private void ThrowIfDisposed()
{
if (!_disposed) return;
ThrowDisposedException();
}
/// <summary>
/// 进入读锁,并在获取锁前后都复核释放状态,确保等待中的线程也能稳定得到容器级异常。
/// </summary>
/// <exception cref="ObjectDisposedException">当容器已释放,或等待期间被其他线程释放时抛出。</exception>
private void EnterReadLockOrThrowDisposed()
{
var lockTaken = false;
try
{
_lock.EnterReadLock();
lockTaken = true;
}
catch (ObjectDisposedException) when (_disposed)
{
ThrowDisposedException();
}
if (!_disposed)
{
return;
}
if (lockTaken)
{
_lock.ExitReadLock();
}
ThrowDisposedException();
}
/// <summary>
/// 进入写锁,并在获取锁前后都复核释放状态,确保等待中的线程不会泄露底层锁异常。
/// </summary>
/// <exception cref="ObjectDisposedException">当容器已释放,或等待期间被其他线程释放时抛出。</exception>
private void EnterWriteLockOrThrowDisposed()
{
var lockTaken = false;
try
{
_lock.EnterWriteLock();
lockTaken = true;
}
catch (ObjectDisposedException) when (_disposed)
{
ThrowDisposedException();
}
if (!_disposed)
{
return;
}
if (lockTaken)
{
_lock.ExitWriteLock();
}
ThrowDisposedException();
}
/// <summary>
/// 在释放标志已经对外可见后,等待遗留 waiter 退场,再尝试释放底层锁。
/// </summary>
/// <remarks>
/// 容器会先把 <see cref="_disposed" /> 置为 <see langword="true" /> 并退出写锁,
/// 这样所有已在等待队列中的线程都能醒来并通过统一路径抛出容器级
/// <see cref="ObjectDisposedException" />。只有当这些线程退场后,底层锁才可安全释放。
/// 该步骤只允许一个释放调用者执行,避免并发 <see cref="Dispose" /> 重复销毁同一个
/// <see cref="ReaderWriterLockSlim" /> 并破坏幂等契约。
/// </remarks>
private void DisposeLockWhenQuiescent()
{
if (Interlocked.CompareExchange(ref _lockDisposalStarted, 1, 0) != 0)
{
return;
}
const int maxDisposeSpinAttempts = 512;
var spinWait = new SpinWait();
for (var attempt = 0; attempt < maxDisposeSpinAttempts; attempt++)
{
if (_lock.CurrentReadCount == 0 &&
_lock.WaitingReadCount == 0 &&
_lock.WaitingWriteCount == 0 &&
_lock.WaitingUpgradeCount == 0)
{
try
{
_lock.Dispose();
return;
}
catch (SynchronizationLockException)
{
// 等待中的线程刚好在本轮检查后切换状态;继续自旋直到锁真正静默。
}
}
spinWait.SpinOnce();
}
_logger.Warn("MicrosoftDiContainer lock disposal was skipped because waiters did not quiesce in time.");
}
/// <summary>
/// 检查容器是否已冻结,如果已冻结则抛出异常
/// 用于保护注册操作的安全性
/// </summary>
/// <exception cref="InvalidOperationException">当容器已冻结时抛出</exception>
private void ThrowIfFrozen()
{
if (!_frozen) return;
const string errorMsg = "MicrosoftDiContainer is frozen";
_logger.Error(errorMsg);
throw new InvalidOperationException(errorMsg);
}
#endregion
/// <summary>
/// 记录某个实例在未冻结查询中可见的服务类型分组信息。
/// </summary>
/// <param name="ServiceType">当前分组对应的服务类型。</param>
/// <param name="Count">该服务类型下的描述符数量。</param>
/// <param name="FirstIndex">该服务类型首次出现的位置,用于稳定打破并列。</param>
private sealed record VisibleServiceTypeGroup(Type ServiceType, int Count, int FirstIndex);
#region Fields
/// <summary>
/// 服务提供者,在容器冻结后构建,用于解析服务实例
/// </summary>
private IServiceProvider? _provider;
/// <summary>
/// 冻结后可复用的服务类型可见性索引。
/// 容器冻结后注册集合不再变化,因此 <see cref="HasRegistration(Type)" /> 可以安全复用该索引。
/// </summary>
private FrozenServiceTypeIndex? _frozenServiceTypeIndex;
/// <summary>
/// 容器冻结状态标志true表示容器已冻结不可修改
/// </summary>
private volatile bool _frozen;
/// <summary>
/// 容器释放状态标志true 表示容器已释放,不允许继续访问。
/// </summary>
private volatile bool _disposed;
/// <summary>
/// 标记底层读写锁的销毁流程是否已经启动,确保并发释放时最多只有一个线程尝试销毁锁实例。
/// </summary>
private int _lockDisposalStarted;
/// <summary>
/// 读写锁,确保多线程环境下的线程安全操作
/// </summary>
private readonly ReaderWriterLockSlim _lock = new(LockRecursionPolicy.NoRecursion);
/// <summary>
/// 已注册实例的集合,用于快速检查实例是否存在
/// </summary>
private readonly HashSet<object> _registeredInstances = [];
/// <summary>
/// 日志记录器,用于记录容器操作日志
/// </summary>
private readonly ILogger _logger = LoggerFactoryResolver.Provider.CreateLogger(nameof(MicrosoftDiContainer));
#endregion
#region Register
/// <summary>
/// 注册单例服务实例
/// 确保同一类型只能注册一次,避免重复注册
/// </summary>
/// <typeparam name="T">服务类型</typeparam>
/// <param name="instance">要注册的实例对象</param>
/// <exception cref="InvalidOperationException">当容器已冻结或类型已被注册时抛出</exception>
public void RegisterSingleton<T>(T instance)
{
ThrowIfDisposed();
var type = typeof(T);
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
// 检查是否已注册该类型,防止重复注册
if (GetServicesUnsafe.Any(s => s.ServiceType == type))
{
var errorMsg = $"Singleton already registered for type: {type.Name}";
_logger.Error(errorMsg);
throw new InvalidOperationException(errorMsg);
}
GetServicesUnsafe.AddSingleton(type, instance!);
_registeredInstances.Add(instance!);
_logger.Debug($"Singleton registered: {type.Name}");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册单例服务,指定服务类型和实现类型
/// 直接使用底层DI容器注册类型映射关系
/// </summary>
/// <typeparam name="TService">服务接口或基类类型</typeparam>
/// <typeparam name="TImpl">具体的实现类型</typeparam>
public void RegisterSingleton<TService, TImpl>()
where TImpl : class, TService
where TService : class
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
GetServicesUnsafe.AddSingleton<TService, TImpl>();
_logger.Debug($"Singleton registered: {typeof(TService).Name}");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册瞬态服务,指定服务类型和实现类型
/// 每次解析时都会创建新的实例
/// </summary>
/// <typeparam name="TService">服务接口或基类类型</typeparam>
/// <typeparam name="TImpl">具体的实现类型</typeparam>
public void RegisterTransient<TService, TImpl>()
where TImpl : class, TService
where TService : class
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
GetServicesUnsafe.AddTransient<TService, TImpl>();
_logger.Debug($"Transient registered: {typeof(TService).Name}");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册作用域服务,指定服务类型和实现类型
/// 在同一作用域内共享实例,不同作用域使用不同实例
/// </summary>
/// <typeparam name="TService">服务接口或基类类型</typeparam>
/// <typeparam name="TImpl">具体的实现类型</typeparam>
public void RegisterScoped<TService, TImpl>()
where TImpl : class, TService
where TService : class
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
GetServicesUnsafe.AddScoped<TService, TImpl>();
_logger.Debug($"Scoped registered: {typeof(TService).Name}");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册多个实例到其所有接口和具体类型
/// 实现一个实例支持多种接口类型的解析
/// </summary>
/// <param name="instance">要注册的对象实例</param>
/// <exception cref="InvalidOperationException">当容器已冻结时抛出</exception>
public void RegisterPlurality(object instance)
{
ThrowIfDisposed();
var concreteType = instance.GetType();
var interfaces = concreteType.GetInterfaces();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
// 注册具体类型映射
GetServicesUnsafe.AddSingleton(concreteType, instance);
// 注册所有接口类型映射(指向同一实例)
foreach (var interfaceType in interfaces)
{
GetServicesUnsafe.AddSingleton(interfaceType, _ => instance);
}
_registeredInstances.Add(instance);
_logger.Debug($"Plurality registered: {concreteType.Name} with {interfaces.Length} interfaces");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册多个实例到其所有接口
/// 实现一个实例支持多种接口类型的解析
/// </summary>
public void RegisterPlurality<T>() where T : class
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
var concreteType = typeof(T);
var interfaces = concreteType.GetInterfaces();
// 注册具体类型
GetServicesUnsafe.AddSingleton<T>();
// 注册所有接口(指向同一个实例)
foreach (var interfaceType in interfaces)
{
GetServicesUnsafe.AddSingleton(interfaceType, sp => sp.GetRequiredService<T>());
}
_logger.Debug($"Type registered: {concreteType.Name} with {interfaces.Length} interfaces");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册系统实例
/// 通过RegisterPlurality方法注册ISystem类型实例
/// </summary>
/// <param name="system">要注册的系统实例</param>
public void RegisterSystem(ISystem system)
{
RegisterPlurality(system);
}
/// <summary>
/// 注册指定泛型类型的服务实例
/// </summary>
/// <typeparam name="T">服务类型</typeparam>
/// <param name="instance">要注册的实例对象</param>
/// <exception cref="InvalidOperationException">当容器已冻结时抛出</exception>
public void Register<T>(T instance)
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
GetServicesUnsafe.AddSingleton(typeof(T), instance!);
_registeredInstances.Add(instance!);
_logger.Debug($"Registered: {typeof(T).Name}");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册指定类型的服务实例
/// </summary>
/// <param name="type">服务类型</param>
/// <param name="instance">要注册的实例对象</param>
/// <exception cref="InvalidOperationException">当容器已冻结时抛出</exception>
public void Register(Type type, object instance)
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
GetServicesUnsafe.AddSingleton(type, instance);
_registeredInstances.Add(instance);
_logger.Debug($"Registered: {type.Name}");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册工厂方法来创建服务实例
/// 通过委托函数动态创建服务实例,支持依赖注入
/// </summary>
/// <typeparam name="TService">服务类型</typeparam>
/// <param name="factory">创建服务实例的工厂委托函数接收IServiceProvider参数</param>
public void RegisterFactory<TService>(
Func<IServiceProvider, TService> factory) where TService : class
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
GetServicesUnsafe.AddSingleton(factory);
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册 CQRS 请求管道行为。
/// 同时支持开放泛型行为类型和已闭合的具体行为类型,
/// 以兼容通用行为和针对单一请求的专用行为两种注册方式。
/// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
public void RegisterCqrsPipelineBehavior<TBehavior>() where TBehavior : class
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
RegisterCqrsPipelineBehaviorCore(
typeof(TBehavior),
typeof(IPipelineBehavior<,>),
"IPipelineBehavior<,>",
"pipeline behavior");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 注册 CQRS 流式请求管道行为。
/// 同时支持开放泛型行为类型和已闭合的具体行为类型,
/// 以兼容通用行为和针对单一流式请求的专用行为两种注册方式。
/// </summary>
/// <typeparam name="TBehavior">行为类型,必须是引用类型</typeparam>
public void RegisterCqrsStreamPipelineBehavior<TBehavior>() where TBehavior : class
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
RegisterCqrsPipelineBehaviorCore(
typeof(TBehavior),
typeof(IStreamPipelineBehavior<,>),
"IStreamPipelineBehavior<,>",
"stream pipeline behavior");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 复用 CQRS 行为注册的开放泛型/封闭接口校验逻辑,
/// 让 request 与 stream 两条入口保持一致的容器注册语义。
/// </summary>
/// <param name="behaviorType">待注册的行为运行时类型。</param>
/// <param name="openGenericInterfaceType">行为必须实现的开放泛型接口类型。</param>
/// <param name="interfaceTypeDisplayName">用于日志与异常的接口显示名称。</param>
/// <param name="registrationLabel">用于日志的注册类别名称。</param>
/// <exception cref="InvalidOperationException"><paramref name="behaviorType" /> 未实现目标行为接口。</exception>
private void RegisterCqrsPipelineBehaviorCore(
Type behaviorType,
Type openGenericInterfaceType,
string interfaceTypeDisplayName,
string registrationLabel)
{
if (behaviorType.IsGenericTypeDefinition)
{
GetServicesUnsafe.AddSingleton(openGenericInterfaceType, behaviorType);
}
else
{
var pipelineInterfaces = behaviorType
.GetInterfaces()
.Where(type => type.IsGenericType &&
type.GetGenericTypeDefinition() == openGenericInterfaceType)
.ToList();
if (pipelineInterfaces.Count == 0)
{
var errorMessage = $"{behaviorType.Name} does not implement {interfaceTypeDisplayName}";
_logger.Error(errorMessage);
throw new InvalidOperationException(errorMessage);
}
// 为每个已闭合的行为接口建立显式映射,支持针对特定请求/响应对的专用行为。
foreach (var pipelineInterface in pipelineInterfaces)
{
GetServicesUnsafe.AddSingleton(pipelineInterface, behaviorType);
}
}
_logger.Debug($"CQRS {registrationLabel} registered: {behaviorType.Name}");
}
/// <summary>
/// 从指定程序集显式注册 CQRS 处理器。
/// </summary>
/// <param name="assembly">包含 CQRS 处理器或生成注册器的程序集。</param>
/// <exception cref="ArgumentNullException"><paramref name="assembly" /> 为 <see langword="null" />。</exception>
/// <exception cref="InvalidOperationException">容器已冻结,无法继续注册 CQRS 处理器。</exception>
public void RegisterCqrsHandlersFromAssembly(Assembly assembly)
{
ArgumentNullException.ThrowIfNull(assembly);
RegisterCqrsHandlersFromAssemblies([assembly]);
}
/// <summary>
/// 从多个程序集显式注册 CQRS 处理器。
/// 同一程序集只会被接入一次,避免默认启动路径与扩展模块重复注册相同 handlers。
/// </summary>
/// <param name="assemblies">要接入的程序集集合。</param>
/// <exception cref="ArgumentNullException"><paramref name="assemblies" /> 为 <see langword="null" />。</exception>
/// <exception cref="ArgumentNullException"><paramref name="assemblies" /> 中存在 <see langword="null" /> 元素。</exception>
/// <exception cref="InvalidOperationException"> 容器已冻结,无法继续注册 CQRS 处理器。</exception>
public void RegisterCqrsHandlersFromAssemblies(IEnumerable<Assembly> assemblies)
{
ArgumentNullException.ThrowIfNull(assemblies);
ThrowIfDisposed();
var assemblyArray = assemblies.ToArray();
foreach (var assembly in assemblyArray)
{
if (assembly is null)
{
throw new ArgumentNullException(nameof(assemblies), "Assemblies collection cannot contain null items.");
}
}
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
ResolveCqrsRegistrationService().RegisterHandlers(assemblyArray);
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 配置服务
/// </summary>
/// <param name="configurator">服务配置委托</param>
public void ExecuteServicesHook(Action<IServiceCollection>? configurator = null)
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
ThrowIfFrozen();
configurator?.Invoke(GetServicesUnsafe);
}
finally
{
_lock.ExitWriteLock();
}
}
#endregion
#region Get
/// <summary>
/// 获取当前容器中已注册的 CQRS 程序集注册协调器。
/// 该方法仅供容器内部在注册阶段使用,因此直接读取服务描述符中的实例绑定,
/// 避免在容器未冻结前依赖完整的服务提供者构建流程。
/// </summary>
/// <returns>已注册的 CQRS 程序集注册协调器实例。</returns>
/// <exception cref="InvalidOperationException">未找到可用的 CQRS 程序集注册协调器实例时抛出。</exception>
private ICqrsRegistrationService ResolveCqrsRegistrationService()
{
var registrationService = CollectRegisteredImplementationInstances(typeof(ICqrsRegistrationService))
.OfType<ICqrsRegistrationService>()
.LastOrDefault();
if (registrationService != null)
return registrationService;
const string errorMessage =
"ICqrsRegistrationService is not visible during the registration stage. Ensure the CQRS runtime module " +
"has been installed and that the registration service is pre-materialized as an instance binding before " +
"registering handlers.";
_logger.Error(errorMessage);
throw new InvalidOperationException(errorMessage);
}
/// <summary>
/// 获取指定泛型类型的服务实例
/// 返回第一个匹配的注册实例如果不存在则返回null
/// </summary>
/// <typeparam name="T">服务类型</typeparam>
/// <returns>服务实例或null</returns>
public T? Get<T>() where T : class
{
ThrowIfDisposed();
EnterReadLockOrThrowDisposed();
try
{
if (_provider == null)
{
return CollectRegisteredImplementationInstances(typeof(T)).OfType<T>().FirstOrDefault();
}
var result = _provider!.GetService<T>();
_logger.Debug(result != null
? $"Retrieved instance: {typeof(T).Name}"
: $"No instance found for type: {typeof(T).Name}");
return result;
}
finally
{
_lock.ExitReadLock();
}
}
/// <summary>
/// 获取指定类型的服务实例
/// 返回第一个匹配的注册实例如果不存在则返回null
/// </summary>
/// <param name="type">服务类型</param>
/// <returns>服务实例或null</returns>
public object? Get(Type type)
{
ArgumentNullException.ThrowIfNull(type);
ThrowIfDisposed();
EnterReadLockOrThrowDisposed();
try
{
if (_provider == null)
{
return CollectRegisteredImplementationInstances(type).FirstOrDefault();
}
var result = _provider!.GetService(type);
if (_logger.IsDebugEnabled())
{
_logger.Debug(result != null
? $"Retrieved instance: {type.Name}"
: $"No instance found for type: {type.Name}");
}
return result;
}
finally
{
_lock.ExitReadLock();
}
}
/// <summary>
/// 获取指定泛型类型的必需服务实例
/// 必须存在且唯一,否则抛出异常
/// </summary>
/// <typeparam name="T">服务类型</typeparam>
/// <returns>唯一的服务实例</returns>
/// <exception cref="InvalidOperationException">当实例不存在或多于一个时抛出</exception>
public T GetRequired<T>() where T : class
{
var list = GetAll<T>();
switch (list.Count)
{
case 0:
var notFoundMsg = $"No instance registered for {typeof(T).Name}";
_logger.Error(notFoundMsg);
throw new InvalidOperationException(notFoundMsg);
case 1:
_logger.Debug($"Retrieved required instance: {typeof(T).Name}");
return list[0];
default:
var multipleMsg = $"Multiple instances registered for {typeof(T).Name}";
_logger.Error(multipleMsg);
throw new InvalidOperationException(multipleMsg);
}
}
/// <summary>
/// 获取指定类型的必需服务实例
/// 必须存在且唯一,否则抛出异常
/// </summary>
/// <param name="type">服务类型</param>
/// <returns>唯一的服务实例</returns>
/// <exception cref="InvalidOperationException">当实例不存在或多于一个时抛出</exception>
public object GetRequired(Type type)
{
var list = GetAll(type);
switch (list.Count)
{
case 0:
var notFoundMsg = $"No instance registered for {type.Name}";
_logger.Error(notFoundMsg);
throw new InvalidOperationException(notFoundMsg);
case 1:
_logger.Debug($"Retrieved required instance: {type.Name}");
return list[0];
default:
var multipleMsg = $"Multiple instances registered for {type.Name}";
_logger.Error(multipleMsg);
throw new InvalidOperationException(multipleMsg);
}
}
/// <summary>
/// 获取指定泛型类型的所有服务实例
/// </summary>
/// <typeparam name="T">服务类型</typeparam>
/// <returns>只读的服务实例列表</returns>
public IReadOnlyList<T> GetAll<T>() where T : class
{
ThrowIfDisposed();
EnterReadLockOrThrowDisposed();
try
{
if (_provider == null)
{
return CollectRegisteredImplementationInstances(typeof(T)).Cast<T>().ToList();
}
var services = _provider!.GetServices<T>().ToList();
if (_logger.IsDebugEnabled())
{
_logger.Debug($"Retrieved {services.Count} instances of {typeof(T).Name}");
}
return services;
}
finally
{
_lock.ExitReadLock();
}
}
/// <summary>
/// 获取指定类型的所有服务实例
/// </summary>
/// <param name="type">服务类型</param>
/// <returns>只读的服务实例列表</returns>
/// <exception cref="ArgumentNullException">当 <paramref name="type" /> 为 <see langword="null" /> 时抛出</exception>
public IReadOnlyList<object> GetAll(Type type)
{
ArgumentNullException.ThrowIfNull(type);
ThrowIfDisposed();
EnterReadLockOrThrowDisposed();
try
{
if (_provider == null)
{
return CollectRegisteredImplementationInstances(type);
}
var services = _provider!.GetServices(type).ToList();
if (_logger.IsDebugEnabled())
{
_logger.Debug($"Retrieved {services.Count} instances of {type.Name}");
}
return services.Where(o => o != null).Cast<object>().ToList();
}
finally
{
_lock.ExitReadLock();
}
}
/// <summary>
/// 在容器未冻结时,从服务描述符中收集当前可直接观察到的实例绑定。
/// </summary>
/// <param name="requestedServiceType">调用方请求的服务类型。</param>
/// <returns>按当前未冻结语义可见的实例列表。</returns>
/// <remarks>
/// 该方法只读取 <see cref="ServiceDescriptor.ImplementationInstance" />,因为未冻结路径不会主动执行工厂方法,
/// 也不会提前构造 <see cref="ServiceDescriptor.ImplementationType" />。
/// 若同一实例同时经由多个可赋值的 <see cref="ServiceDescriptor.ServiceType" /> 暴露,
/// 这里会把它视为兼容别名并只保留一个规范服务类型对应的结果;
/// 但同一 <see cref="ServiceDescriptor.ServiceType" /> 的重复显式注册仍会完整保留,以维持注册顺序和多次注册语义。
/// </remarks>
private List<object> CollectRegisteredImplementationInstances(Type requestedServiceType)
{
ArgumentNullException.ThrowIfNull(requestedServiceType);
var matchingDescriptors = GetServicesUnsafe
.Where(descriptor =>
descriptor.ServiceType == requestedServiceType ||
requestedServiceType.IsAssignableFrom(descriptor.ServiceType))
.ToList();
if (matchingDescriptors.Count == 0)
return [];
var preferredServiceTypes = BuildPreferredVisibleServiceTypes(matchingDescriptors, requestedServiceType);
var result = new List<object>();
foreach (var descriptor in matchingDescriptors)
{
if (descriptor.ImplementationInstance is { } instance)
{
if (preferredServiceTypes.TryGetValue(instance, out var preferredServiceType) &&
preferredServiceType == descriptor.ServiceType)
{
result.Add(instance);
}
}
else if (descriptor.ImplementationFactory != null)
{
// 在未冻结状态下无法调用工厂方法,跳过。
}
else if (descriptor.ImplementationType != null)
{
// 在未冻结状态下无法创建实例,跳过。
}
}
return result;
}
/// <summary>
/// 为每个可见实例选择一个规范服务类型,避免同一实例因兼容别名重复出现在未冻结查询结果中。
/// </summary>
/// <param name="matchingDescriptors">已按请求类型过滤过的服务描述符集合。</param>
/// <param name="requestedServiceType">调用方请求的服务类型。</param>
/// <returns>实例到其规范服务类型的映射。</returns>
private static Dictionary<object, Type> BuildPreferredVisibleServiceTypes(
IReadOnlyList<ServiceDescriptor> matchingDescriptors,
Type requestedServiceType)
{
var preferredServiceTypes = new Dictionary<object, Type>(ReferenceEqualityComparer.Instance);
foreach (var instanceGroup in matchingDescriptors
.Where(static descriptor => descriptor.ImplementationInstance is not null)
.GroupBy(static descriptor => descriptor.ImplementationInstance!,
ReferenceEqualityComparer.Instance))
{
preferredServiceTypes.Add(
instanceGroup.Key,
SelectPreferredVisibleServiceType(instanceGroup, requestedServiceType));
}
return preferredServiceTypes;
}
/// <summary>
/// 在“同一实例被多个服务类型暴露”的场景下,选择未冻结查询结果应保留的规范服务类型。
/// </summary>
/// <param name="descriptorsForInstance">引用同一实例的服务描述符。</param>
/// <param name="requestedServiceType">调用方请求的服务类型。</param>
/// <returns>应在结果中保留的服务类型。</returns>
private static Type SelectPreferredVisibleServiceType(
IEnumerable<ServiceDescriptor> descriptorsForInstance,
Type requestedServiceType)
{
var serviceTypeGroups = descriptorsForInstance
.GroupBy(static descriptor => descriptor.ServiceType)
.Select((group, index) => new VisibleServiceTypeGroup(group.Key, group.Count(), index))
.ToList();
// 若调用方请求的正是其中一个服务类型,优先保留它,使未冻结行为尽量贴近冻结后的精确服务解析口径。
var requestedGroup = serviceTypeGroups.FirstOrDefault(group => group.ServiceType == requestedServiceType);
if (requestedGroup is not null)
return requestedGroup.ServiceType;
// 否则优先保留“同一服务类型下注册次数最多”的那组,避免显式多次注册被较宽泛的别名折叠掉。
return serviceTypeGroups
.OrderByDescending(static group => group.Count)
.ThenBy(static group => group.FirstIndex)
.First()
.ServiceType;
}
/// <summary>
/// 获取并排序指定泛型类型的所有服务实例
/// 主要用于系统调度场景
/// </summary>
/// <typeparam name="T">服务类型</typeparam>
/// <param name="comparison">比较委托,用于定义排序规则</param>
/// <returns>排序后的只读服务实例列表</returns>
public IReadOnlyList<T> GetAllSorted<T>(Comparison<T> comparison) where T : class
{
ThrowIfDisposed();
var list = GetAll<T>().ToList();
list.Sort(comparison);
return list;
}
/// <summary>
/// 获取指定类型的所有实例,并按优先级排序
/// 实现 IPrioritized 接口的服务将按优先级排序(数值越小优先级越高)
/// 未实现 IPrioritized 的服务将使用默认优先级 0
/// </summary>
/// <typeparam name="T">期望获取的实例类型</typeparam>
/// <returns>按优先级排序后的实例列表</returns>
public IReadOnlyList<T> GetAllByPriority<T>() where T : class
{
var services = GetAll<T>();
return SortByPriority(services);
}
/// <summary>
/// 获取指定类型的所有实例,并按优先级排序
/// 实现 IPrioritized 接口的服务将按优先级排序(数值越小优先级越高)
/// 未实现 IPrioritized 的服务将使用默认优先级 0
/// </summary>
/// <param name="type">期望获取的实例类型</param>
/// <returns>按优先级排序后的实例列表</returns>
public IReadOnlyList<object> GetAllByPriority(Type type)
{
var services = GetAll(type);
return SortByPriority(services);
}
/// <summary>
/// 按优先级排序服务列表
/// 实现 IPrioritized 接口的服务按 Priority 属性排序(升序)
/// 未实现接口的服务使用默认优先级 0
/// 相同优先级保持原有注册顺序(稳定排序)
/// </summary>
private static IReadOnlyList<T> SortByPriority<T>(IReadOnlyList<T> services) where T : class
{
if (services.Count <= 1)
return services;
// 使用 OrderBy 确保稳定排序(相同优先级保持原有顺序)
return services
.Select((service, index) => new { Service = service, Index = index })
.OrderBy(x =>
{
var priority = x.Service is IPrioritized p ? p.Priority : 0;
return (priority, x.Index); // 先按优先级,再按索引
})
.Select(x => x.Service)
.ToList();
}
#endregion
#region Utility
/// <summary>
/// 检查容器中是否包含指定泛型类型的实例
/// 根据容器状态选择不同的检查策略
/// </summary>
/// <typeparam name="T">要检查的类型</typeparam>
/// <returns>true表示包含该类型实例false表示不包含</returns>
public bool Contains<T>() where T : class
{
ThrowIfDisposed();
EnterReadLockOrThrowDisposed();
try
{
if (_provider == null)
return GetServicesUnsafe.Any(s => s.ServiceType == typeof(T));
return _provider.GetService<T>() != null;
}
finally
{
_lock.ExitReadLock();
}
}
/// <summary>
/// 检查容器中是否存在可赋值给指定服务类型的注册项,而不要求先解析实例。
/// </summary>
/// <param name="type">要检查的服务类型。</param>
/// <returns>若存在显式注册或开放泛型映射可满足该服务类型,则返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
public bool HasRegistration(Type type)
{
ArgumentNullException.ThrowIfNull(type);
ThrowIfDisposed();
EnterReadLockOrThrowDisposed();
try
{
if (_frozenServiceTypeIndex is not null)
{
return _frozenServiceTypeIndex.Contains(type);
}
return HasRegistrationCore(type);
}
finally
{
_lock.ExitReadLock();
}
}
/// <summary>
/// 判断容器中是否包含某个具体的实例对象
/// 通过已注册实例集合进行快速查找
/// </summary>
/// <param name="instance">要检查的实例对象</param>
/// <returns>true表示包含该实例false表示不包含</returns>
public bool ContainsInstance(object instance)
{
ThrowIfDisposed();
EnterReadLockOrThrowDisposed();
try
{
return _registeredInstances.Contains(instance);
}
finally
{
_lock.ExitReadLock();
}
}
/// <summary>
/// 在当前容器状态下检查指定服务类型是否存在可见注册。
/// </summary>
/// <param name="requestedType">要检查的服务类型。</param>
/// <returns>存在可满足该类型的注册时返回 <see langword="true" />;否则返回 <see langword="false" />。</returns>
/// <remarks>
/// 该检查只回答“是否可能解析到服务”,不会为了判断结果而激活实例。
/// 预冻结阶段只基于当前服务描述符推断;冻结后则同样只观察描述符,
/// 避免把瞬态/多实例解析成本混入热路径中的存在性判断。
/// </remarks>
private bool HasRegistrationCore(Type requestedType)
{
foreach (var descriptor in GetServicesUnsafe)
{
if (CanSatisfyServiceType(descriptor.ServiceType, requestedType))
{
return true;
}
}
return false;
}
/// <summary>
/// 判断某个服务描述符声明的服务类型是否能满足当前请求类型。
/// </summary>
/// <param name="registeredServiceType">注册时声明的服务类型。</param>
/// <param name="requestedType">调用方请求的服务类型。</param>
/// <returns>若当前注册可用于解析 <paramref name="requestedType" />,则返回 <see langword="true" />。</returns>
private static bool CanSatisfyServiceType(Type registeredServiceType, Type requestedType)
{
// 这里刻意与 Get/GetAll 的“按服务键解析”语义保持一致:
// 只有注册时声明的服务类型本身命中,或开放泛型服务键能闭合到请求类型时,才视为存在可见注册。
if (registeredServiceType == requestedType)
{
return true;
}
if (requestedType.IsConstructedGenericType && registeredServiceType.IsGenericTypeDefinition)
{
return requestedType.GetGenericTypeDefinition() == registeredServiceType;
}
return false;
}
/// <summary>
/// 清空容器中的所有实例和服务注册
/// 只有在容器未冻结状态下才能执行清空操作
/// </summary>
public void Clear()
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
// 冻结的容器不允许清空操作
if (_frozen)
{
_logger.Warn("Cannot clear frozen container");
return;
}
// 未冻结的容器不会构建根 ServiceProvider因此这里仅重置注册状态即可。
GetServicesUnsafe.Clear();
_registeredInstances.Clear();
_provider = null;
_frozenServiceTypeIndex = null;
_frozen = false;
_logger.Info("Container cleared");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 冻结容器并构建ServiceProvider
/// 冻结后容器变为只读状态,不能再注册新服务
/// </summary>
public void Freeze()
{
ThrowIfDisposed();
EnterWriteLockOrThrowDisposed();
try
{
// 防止重复冻结
if (_frozen)
{
_logger.Warn("Container already frozen");
return;
}
_provider = GetServicesUnsafe.BuildServiceProvider();
_frozenServiceTypeIndex = FrozenServiceTypeIndex.Create(GetServicesUnsafe);
_frozen = true;
_logger.Info("IOC Container frozen - ServiceProvider built");
}
finally
{
_lock.ExitWriteLock();
}
}
/// <summary>
/// 保存冻结后按服务键可见的精确服务类型与开放泛型定义集合。
/// </summary>
/// <remarks>
/// 该索引只回答“按当前服务键语义是否可见”,因此与 <see cref="Get(Type)" /> /
/// <see cref="GetAll(Type)" /> 一样不会退化为更宽松的可赋值匹配。
/// </remarks>
private sealed class FrozenServiceTypeIndex(HashSet<Type> exactServiceTypes, HashSet<Type> openGenericServiceTypes)
{
private readonly HashSet<Type> _exactServiceTypes = exactServiceTypes;
private readonly HashSet<Type> _openGenericServiceTypes = openGenericServiceTypes;
/// <summary>
/// 基于冻结时最终确定的服务描述符集合创建索引。
/// </summary>
/// <param name="descriptors">冻结时的服务描述符序列。</param>
/// <returns>供存在性判断热路径复用的服务键索引。</returns>
public static FrozenServiceTypeIndex Create(IEnumerable<ServiceDescriptor> descriptors)
{
ArgumentNullException.ThrowIfNull(descriptors);
var exactServiceTypes = new HashSet<Type>();
var openGenericServiceTypes = new HashSet<Type>();
foreach (var descriptor in descriptors)
{
var serviceType = descriptor.ServiceType;
exactServiceTypes.Add(serviceType);
if (serviceType.IsGenericTypeDefinition)
{
openGenericServiceTypes.Add(serviceType);
}
}
return new FrozenServiceTypeIndex(exactServiceTypes, openGenericServiceTypes);
}
/// <summary>
/// 判断当前索引是否声明了目标服务键。
/// </summary>
/// <param name="requestedType">要检查的服务类型。</param>
/// <returns>命中精确服务键或可闭合的开放泛型服务键时返回 <see langword="true" />。</returns>
public bool Contains(Type requestedType)
{
ArgumentNullException.ThrowIfNull(requestedType);
return _exactServiceTypes.Contains(requestedType) ||
requestedType.IsConstructedGenericType &&
_openGenericServiceTypes.Contains(requestedType.GetGenericTypeDefinition());
}
}
/// <summary>
/// 获取底层的服务集合
/// 提供对内部IServiceCollection的访问权限用于高级配置和自定义操作
/// </summary>
/// <returns>底层的IServiceCollection实例</returns>
public IServiceCollection GetServicesUnsafe { get; } = serviceCollection ?? new ServiceCollection();
/// <summary>
/// 创建一个新的服务作用域
/// 作用域内的 Scoped 服务将共享同一实例
/// </summary>
/// <returns>服务作用域实例</returns>
/// <exception cref="InvalidOperationException">当容器未冻结时抛出</exception>
public IServiceScope CreateScope()
{
ThrowIfDisposed();
EnterReadLockOrThrowDisposed();
try
{
// 在锁内检查,避免竞态条件
if (!_frozen || _provider == null)
{
const string errorMsg = "Cannot create scope before container is frozen";
_logger.Error(errorMsg);
throw new InvalidOperationException(errorMsg);
}
var scope = _provider.CreateScope();
_logger.Debug("Service scope created");
return scope;
}
finally
{
_lock.ExitReadLock();
}
}
/// <summary>
/// 释放容器持有的服务提供者、注册状态和同步原语。
/// </summary>
/// <remarks>
/// 冻结后的根 <see cref="IServiceProvider" /> 会拥有 DI 创建的单例与作用域根缓存,因此 benchmark、
/// 测试宿主或短生命周期架构在结束时需要显式释放容器,避免这些对象与内部
/// <see cref="ReaderWriterLockSlim" /> 一起滞留。
/// 释放是幂等的;首次释放后所有后续访问都会抛出 <see cref="ObjectDisposedException" />。
/// </remarks>
public void Dispose()
{
if (_disposed)
{
return;
}
var lockTaken = false;
try
{
try
{
_lock.EnterWriteLock();
lockTaken = true;
}
catch (ObjectDisposedException) when (_disposed)
{
return;
}
if (_disposed)
{
return;
}
_disposed = true;
(_provider as IDisposable)?.Dispose();
_provider = null;
_frozenServiceTypeIndex = null;
GetServicesUnsafe.Clear();
_registeredInstances.Clear();
_frozen = false;
_logger.Info("IOC Container disposed");
}
finally
{
if (lockTaken)
{
_lock.ExitWriteLock();
DisposeLockWhenQuiescent();
}
}
}
#endregion
}