ASP.NET Core DI IOC

170 阅读9分钟

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 方式

  1. 构造函数注入
  2. 通过HttpContext.RequestServices 属性来获取当前请求的服务容器 IServiceProvider,scope为单次请求
  3. [FromServices] 方式从参数注入

1.4 生命周期

  1. Single
    挂靠在root provider上
  2. Scope
    每个scope 一个,挂靠在Scope上
  3. 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> 会获取所有注册的实例

构造函数选择策略

  1. 被选择的构造函数具有一个基本条件:IServiceProvider对象能够提供构造函数的所有参数
  2. 构造函数参数数量越多,权重越大。 如果有多个构造函数满足条件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[] 提供。
    • 参数按顺序匹配(非按名称)。
(3) 替代 new 的关键场景
  • 无法直接 new 的对象(依赖项需从容器获取)。
  • 动态类型创建(如通过 Type 反射实例化)。
  • 第三方库集成(需手动构造但希望保持 DI 兼容性)。

实现原理

ActivatorUtilities 内部通过以下步骤工作:

  1. 分析构造函数:选择最匹配的构造函数(优先选择参数最多的)。

  2. 混合参数来源

    • 从 IServiceProvider 解析已知依赖。
    • 从 params object[] 填充剩余参数。
  3. 缓存优化:生成动态编译的工厂方法(避免反射性能损耗)。

构造函数选择规则

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有两个实现类

  1. ServiceProvider, ServiceCollection create 出来的是这个, 是root provider/ site collection 类似这种概念
  2. 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;
    }
}

示例代码实现