1. 使用方式
1.1 依赖
Microsoft.Extensions.DependencyInjection
1.2 单独使用
ServiceCollection services = new ServiceCollection();
services.AddSingleton<IDItest>(new DITestImpl());
ServiceProvider serviceProvider = services.BuildServiceProvider();
IDItest test = serviceProvider.GetService<IDItest>();
using(var scope = serviceProvider.CreateScope())
{
IDItest test2 = scope.ServiceProvider.GetService<IDItest>();
}
1.3 ASP.NET Core
使用
public static void Main(string[] args)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<IDItest>(new DITestImpl());
var app = builder.Build();
app.Run();
}
[HttpGet("test")]
public string Test([FromServices] IDItest test)
{
return test.GetCarName() ;
}
DI 方式
- 构造函数注入
- 通过HttpContext.RequestServices 属性来获取当前请求的服务容器 IServiceProvider,scope为单次请求
- [FromServices] 方式从参数注入
1.4 生命周期
- Single
挂靠在root provider上 - Scope
每个scope 一个,挂靠在Scope上 - Transient
每次获取都新建一个,如果实现了IDisposal,挂靠在Scope上
管理服务周期
创建出来对象的服务周期由IServiceProvider管理, 如果某个服务实例的类型实现了IDisposal 或 IAsyncDsposalable接口,意味着当前生命周期完结时需要调用Disposal释放资源。
Transient 和 Scoped : 所有实现了IDisposable 接口的服务实例会被当前IServiceProvider对象保存,当IServiceProvider对象的Dispose方法被调用是,服务实例的Dispose方法会随之被调用
Singleton:服务实例保存在root serviceProvider上,root 释放时跟着释放
IAsyncDsiposalable
IDisposalAble接口和 IAsyncDisposable接口实现类型的实例都会被添加到DisposalAble Services列表中,但是当ISyncScope对象的Dsiposal方法被执行时,如果待释放服务实例对应的类型仅仅实现了IAsyncDisposalAble接口,没有实现IDisposalable接口,会抛出一个InvalidOperationException一场
容器的释放具有同步异步两种方式,由服务范围实例决定,当以异步方式释放容器时,可以采用同步的方式释放服务实例,反之则不成立。
这种情况下,应使用AsyncServiceScope对象, new ServiceCollection().CreateAsyncScope();
1.5 服务注册
添加的服务被保存在IServiceCollection表示的集合中,并通过集合创建表示依赖注入容器的IServiceProvider
可以为同一个类型注册多个服务
可以为同一个类型添加多个服务注册,所有服务注册都是有效的,但是GetService<>总是返回最后注册的服务。
GetServices<>用来返回指定类型注册的所有实例
可以注册未指定类型泛型类型
ServiceCollection services = new ServiceCollection();
services.AddSingleton(typeof(IDictionary<,>), typeof(Dictionary<,>));
ServiceProvider serviceProvider = services.BuildServiceProvider();
serviceProvider.GetService<IDictionary<string, int>>();
DI8.0 可配置服务的Key
serviceCollection.AddKeyedScoped
Dependency Injection 8.0新功能——KeyedService - 寻己Tenleft - 博客园
服务验证
如果Single 使用了 Scope 类型实例, 将导致Scope 类型实例无法回收, 框架提供了验证机制
ServiceProvider serviceProvider = services.BuildServiceProvider(true);
true表示验证服务范围有效性
1.6 服务获取
可以为同一个类型添加多个服务实例,所有服务注册都是有效的
GetService<T> 只会获取最后一个注册的实例
GetServices<T> 会获取所有注册的实例
构造函数选择策略
- 被选择的构造函数具有一个基本条件:IServiceProvider对象能够提供构造函数的所有参数
- 构造函数参数数量越多,权重越大。 如果有多个构造函数满足条件1 且 权重都是最大权重, 会抛出InvalidOperationException异常
创建未注册的类型实例(From ai, skip first about detail)
ActivatorUtilities 是 .NET 依赖注入 (DI) 系统中的一个 工具类,位于 Microsoft.Extensions.DependencyInjection 命名空间,用于在 不直接依赖 DI 容器 的情况下创建对象实例,同时支持 构造函数依赖注入。它常用于需要手动控制实例创建,但仍希望利用 DI 机制的场景,如MVC 创建controller、 ILogger创建。创建的实例 不受 DI 容器生命周期管理
CreateInstance<T>(IServiceProvider, params object[]) | 创建 T 的实例,自动注入 DI 容器中的依赖,并支持额外参数 |
|---|---|
CreateInstance(IServiceProvider, Type, params object[]) | 同上,但通过 Type 指定类型 |
GetServiceOrCreateInstance<T>(IServiceProvider) | 优先从 DI 容器获取 T,若未注册则自动创建 |
GetServiceOrCreateInstance(IServiceProvider, Type) | 同上, |
ActivatorUtilities.CreateInstance<DITestImpl>(serviceProvider, 1);
//
// 摘要:
// Instantiate a type with constructor arguments provided directly and/or from an
// System.IServiceProvider.
//
// 参数:
// provider:
// The service provider used to resolve dependencies
//
// parameters:
// Constructor arguments not provided by the provider.
//
// 类型参数:
// T:
// The type to activate
//
// 返回结果:
// An activated object of type T
public static T CreateInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(IServiceProvider provider, params object[] parameters)
{
return (T)CreateInstance(provider, typeof(T), parameters);
}
典型使用场景
(1) 动态创建依赖注入对象
public class MyService
{
private readonly ILogger _logger;
public MyService(ILogger<MyService> logger) => _logger = logger;
}
// 使用 ActivatorUtilities 创建实例(自动注入 ILogger)
var service = ActivatorUtilities.CreateInstance<MyService>(serviceProvider);
- 优点:即使
MyService未提前注册到 DI 容器,也能自动解析其依赖(如ILogger)。
(2) 混合手动参数与依赖注入
public class ReportGenerator
{
private readonly IDataService _dataService;
public ReportGenerator(IDataService dataService, string reportName)
{
_dataService = dataService;
ReportName = reportName;
}
}
// 手动传递 reportName,自动注入 IDataService
var reportGen = ActivatorUtilities.CreateInstance<ReportGenerator>(
serviceProvider,
"MonthlyReport" // 额外参数
);
-
规则:
- 构造函数中 未被 DI 容器覆盖的参数 必须通过
params object[]提供。 - 参数按顺序匹配(非按名称)。
- 构造函数中 未被 DI 容器覆盖的参数 必须通过
(3) 替代 new 的关键场景
- 无法直接
new的对象(依赖项需从容器获取)。 - 动态类型创建(如通过
Type反射实例化)。 - 第三方库集成(需手动构造但希望保持 DI 兼容性)。
实现原理
ActivatorUtilities 内部通过以下步骤工作:
-
分析构造函数:选择最匹配的构造函数(优先选择参数最多的)。
-
混合参数来源:
- 从
IServiceProvider解析已知依赖。 - 从
params object[]填充剩余参数。
- 从
-
缓存优化:生成动态编译的工厂方法(避免反射性能损耗)。
构造函数选择规则
ActivatorUtilities 在创建实例时,会优先选择 参数最多的构造函数(贪婪算法),但必须满足以下条件:
-
所有参数都能被满足:
- 参数要么能从
IServiceProvider中解析(已注册的服务), - 要么由
params object[]手动提供。
- 参数要么能从
示例代码
public class MyService
{
// 构造函数A:2个参数(ILogger 和 string)
public MyService(ILogger<MyService> logger, string name) { }
// 构造函数B:1个参数(ILogger)
public MyService(ILogger<MyService> logger) { }
}
// 使用 ActivatorUtilities 创建实例
var service = ActivatorUtilities.CreateInstance<MyService>(
serviceProvider,
"TestName" // 手动提供 string 参数
);
- 结果:选择 构造函数A(参数更多,且
ILogger可从容器获取,string由手动参数提供)。
特殊情况
- 如果存在多个参数数量相同的构造函数,会抛出
InvalidOperationException(无法自动选择)。 - 如果没有任何构造函数的参数能被满足,抛出
InvalidOperationException。
参数匹配规则
参数顺序必须严格匹配
手动提供的 params object[] 参数 按构造函数参数的声明顺序 匹配,而非按名称或类型推导。
public class ReportService
{
public ReportService(IDataService data, string title, int priority) { }
}
// 正确:手动参数按顺序匹配 title 和 priority
var report = ActivatorUtilities.CreateInstance<ReportService>(
serviceProvider,
"AnnualReport", // 对应 title
100 // 对应 priority
);
// 错误:顺序不匹配会导致运行时异常
var report = ActivatorUtilities.CreateInstance<ReportService>(
serviceProvider,
100, // 错误:int 无法赋值给 string
"AnnualReport"
);
混合依赖注入和手动参数
- DI 容器优先:
参数首先尝试从IServiceProvider解析,失败时再从手动参数中按顺序获取。 - 必须覆盖所有参数:
如果某个参数既无法从容器解析,又无手动提供,会抛出异常。
2. 对象关系/原理
具体的服务注册体现为同一个ServiceDescriptor对象
IServiceCollection 对象是一个存放服务注册信息的集合,IServiceCollection 继承IList<ServiceDescriptor>
依赖注入容器的IServiceProvider 对象是由IServiceCollection 对象构建的,且包含了IServiceCollection属性。
ServiceDescriptor
public class ServiceDescriptor
{
//生命周期
public ServiceLifetime Lifetime { get; }
//DI 8.0新增, 对应serviceCollection.AddKeyedScoped方法
public object? ServiceKey { get; }
//实例注册类型
public Type ServiceType { get; }
//服务的实现类型
public Type? ImplementationType => IsKeyedService ? null : _implementationType;
//实现实例
public object? ImplementationInstance => IsKeyedService ? null : _implementationInstance;
//工厂方法
public Func<IServiceProvider, object>? ImplementationFactory => IsKeyedService ? null : (Func<IServiceProvider, object>?)_implementationFactory;
}
ServiceCollection
/// Specifies the contract for a collection of service descriptors.
/// </summary>
public interface IServiceCollection : IList<ServiceDescriptor>
{
}
public class ServiceCollection : IServiceCollection
{
private readonly List<ServiceDescriptor> _descriptors = new List<ServiceDescriptor>();
public int Count => _descriptors.Count;
public ServiceDescriptor this[int index]
{
get
{
return _descriptors[index];
}
set
{
_descriptors[index] = value;
}
}
void ICollection<ServiceDescriptor>.Add(ServiceDescriptor item)
{
_descriptors.Add(item);
}
}
IServiceProvider
IServiceProvider有两个实现类
- ServiceProvider, ServiceCollection create 出来的是这个, 是root provider/ site collection 类似这种概念
- ServiceProviderEngineScope
实际提供、缓存Service 的实现类。
ServiceProvider 持有表示root 的Scope, 子scope 都是创建ServiceProviderEngineScope, 不会再创建ServiceProvider
ServiceProvider
public sealed class ServiceProvider : IServiceProvider, IKeyedServiceProvider, IDisposable, IAsyncDisposable
{
//Service Collection
internal CallSiteFactory CallSiteFactory { get; }
//root scopt/ service provider
internal ServiceProviderEngineScope Root { get; }
internal ServiceProvider(ICollection<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
{
// note that Root needs to be set before calling GetEngine(), because the engine may need to access Root
Root = new ServiceProviderEngineScope(this, isRootScope: true);
_engine = GetEngine();
_createServiceAccessor = CreateServiceAccessor;
_serviceAccessors = new ConcurrentDictionary<ServiceIdentifier, ServiceAccessor>();
CallSiteFactory = new CallSiteFactory(serviceDescriptors);
}
internal IServiceScope CreateScope()
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
return new ServiceProviderEngineScope(this, isRootScope: false);
}
public object? GetService(Type serviceType) => GetService(ServiceIdentifier.FromServiceType(serviceType), Root);
internal object? GetService(ServiceIdentifier serviceIdentifier, ServiceProviderEngineScope serviceProviderEngineScope)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
ServiceAccessor serviceAccessor = _serviceAccessors.GetOrAdd(serviceIdentifier, _createServiceAccessor);
OnResolve(serviceAccessor.CallSite, serviceProviderEngineScope);
DependencyInjectionEventSource.Log.ServiceResolved(this, serviceIdentifier.ServiceType);
object? result = serviceAccessor.RealizedService?.Invoke(serviceProviderEngineScope);
System.Diagnostics.Debug.Assert(result is null || CallSiteFactory.IsService(serviceIdentifier));
return result;
}
public object? GetKeyedService(Type serviceType, object? serviceKey)
=> GetKeyedService(serviceType, serviceKey, Root);
internal object? GetKeyedService(Type serviceType, object? serviceKey, ServiceProviderEngineScope serviceProviderEngineScope)
{
if (serviceKey == KeyedService.AnyKey)
{
if (!serviceType.IsGenericType || serviceType.GetGenericTypeDefinition() != typeof(IEnumerable<>))
{
ThrowHelper.ThrowInvalidOperationException_KeyedServiceAnyKeyUsedToResolveService();
}
}
return GetService(new ServiceIdentifier(serviceKey, serviceType), serviceProviderEngineScope);
}
}
ServiceProviderEngineScope
internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IKeyedServiceProvider, IAsyncDisposable, IServiceScopeFactory
{
// 可释放的服务, scope disposal 的时候跟着一起disposal
internal IList<object> Disposables => _disposables ?? (IList<object>)Array.Empty<object>();
private bool _disposed;
private List<object>? _disposables;
//provider 是 root provider、Service collection这种概念
public ServiceProviderEngineScope(ServiceProvider provider, bool isRootScope)
{
ResolvedServices = new Dictionary<ServiceCacheKey, object?>();
RootProvider = provider;
IsRootScope = isRootScope;
}
//缓存同一个 Scope 内多次请求的同一服务,避免重复构造。
internal Dictionary<ServiceCacheKey, object?> ResolvedServices { get; }
internal bool Disposed => _disposed;
// This lock protects state on the scope, in particular, for the root scope, it protects
// the list of disposable entries only, since ResolvedServices are cached on CallSites
// For other scopes, it protects ResolvedServices and the list of disposables
internal object Sync => ResolvedServices;
public bool IsRootScope { get; }
internal ServiceProvider RootProvider { get; }
public object? GetService(Type serviceType)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
return RootProvider.GetService(ServiceIdentifier.FromServiceType(serviceType), this);
}
public object? GetKeyedService(Type serviceType, object? serviceKey)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
return RootProvider.GetKeyedService(serviceType, serviceKey, this);
}
public object GetRequiredKeyedService(Type serviceType, object? serviceKey)
{
if (_disposed)
{
ThrowHelper.ThrowObjectDisposedException();
}
return RootProvider.GetRequiredKeyedService(serviceType, serviceKey, this);
}
//干活的provider 就是当前类
public IServiceProvider ServiceProvider => this;
//每个scope都是直属于root的?
public IServiceScope CreateScope() => RootProvider.CreateScope();
//判定service是不是disposalable , 如果是就加入disposal able缓存
internal object? CaptureDisposable(object? service)
{
if (ReferenceEquals(this, service) || !(service is IDisposable || service is IAsyncDisposable))
{
return service;
}
bool disposed = false;
lock (Sync)
{
if (_disposed)
{
disposed = true;
}
else
{
_disposables ??= new List<object>();
_disposables.Add(service);
}
}
// Don't run customer code under the lock
if (disposed)
{
if (service is IDisposable disposable)
{
disposable.Dispose();
}
else
{
// sync over async, for the rare case that an object only implements IAsyncDisposable and may end up starving the thread pool.
object? localService = service; // copy to avoid closure on other paths
Task.Run(() => ((IAsyncDisposable)localService).DisposeAsync().AsTask()).GetAwaiter().GetResult();
}
ThrowHelper.ThrowObjectDisposedException();
}
return service;
}
//所有service disposal
public void Dispose()
{
List<object>? toDispose = BeginDispose();
if (toDispose != null)
{
for (int i = toDispose.Count - 1; i >= 0; i--)
{
if (toDispose[i] is IDisposable disposable)
{
disposable.Dispose();
}
else
{
throw new InvalidOperationException(SR.Format(SR.AsyncDisposableServiceDispose, TypeNameHelper.GetTypeDisplayName(toDispose[i])));
}
}
}
}
public ValueTask DisposeAsync()
{
List<object>? toDispose = BeginDispose();
if (toDispose != null)
{
try
{
for (int i = toDispose.Count - 1; i >= 0; i--)
{
object disposable = toDispose[i];
if (disposable is IAsyncDisposable asyncDisposable)
{
ValueTask vt = asyncDisposable.DisposeAsync();
if (!vt.IsCompletedSuccessfully)
{
return Await(i, vt, toDispose);
}
// If its a IValueTaskSource backed ValueTask,
// inform it its result has been read so it can reset
vt.GetAwaiter().GetResult();
}
else
{
((IDisposable)disposable).Dispose();
}
}
}
catch (Exception ex)
{
return new ValueTask(Task.FromException(ex));
}
}
return default;
static async ValueTask Await(int i, ValueTask vt, List<object> toDispose)
{
await vt.ConfigureAwait(false);
// vt is acting on the disposable at index i,
// decrement it and move to the next iteration
i--;
for (; i >= 0; i--)
{
object disposable = toDispose[i];
if (disposable is IAsyncDisposable asyncDisposable)
{
await asyncDisposable.DisposeAsync().ConfigureAwait(false);
}
else
{
((IDisposable)disposable).Dispose();
}
}
}
}
private List<object>? BeginDispose()
{
lock (Sync)
{
if (_disposed)
{
return null;
}
// Track statistics about the scope (number of disposable objects and number of disposed services)
DependencyInjectionEventSource.Log.ScopeDisposed(RootProvider.GetHashCode(), ResolvedServices.Count, _disposables?.Count ?? 0);
// We've transitioned to the disposed state, so future calls to
// CaptureDisposable will immediately dispose the object.
// No further changes to _state.Disposables, are allowed.
_disposed = true;
}
if (IsRootScope && !RootProvider.IsDisposed())
{
// If this ServiceProviderEngineScope instance is a root scope, disposing this instance will need to dispose the RootProvider too.
// Otherwise the RootProvider will never get disposed and will leak.
// Note, if the RootProvider get disposed first, it will automatically dispose all attached ServiceProviderEngineScope objects.
RootProvider.Dispose();
}
// ResolvedServices is never cleared for singletons because there might be a compilation running in background
// trying to get a cached singleton service. If it doesn't find it
// it will try to create a new one which will result in an ObjectDisposedException.
return _disposables;
}
}