Microsoft.Extensions.DependencyInjection 入门
2025-07-01 20:55:10

Microsoft.Extensions.DependencyInjection 是 .NET Core 中用于依赖注入的库。
但是它也支持 .NET Framework。

#快速开始

1
2
3
4
5
6
IServiceProvider serviceProvider = new ServiceCollection()
.AddTransient<IService, Service>()
.BuildServiceProvider();

var service = serviceProvider.GetService<IService>();
service.PrintMessage();

IServiceCollection:用于注册应用程序所需的服务。并通过BuildServiceProvider方法构建ServiceProvider
IServiceProvider:提供服务的容器。构建ServiceProvider时,IServiceCollection中注册的所有服务都会被实例化,并按照其生命周期进行管理。

#注册服务

  • 以三种不同的生命周期方式注册:
1
2
3
AddSingleton<IService, Service>();
AddScoped<IService, Service>();
AddTransient<IService, Service>();

生命周期内容可以看下一节。

  • 支持带Key注册服务,以便区分不同的实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private static void Foo()
{
var serviceCollection = new ServiceCollection();
var serviceProvider = serviceCollection
.AddKeyedTransient<IServiceA, ServiceA1>("A1")
.AddKeyedTransient<IServiceA, ServiceA2>("A2")
.BuildServiceProvider();

var obj = serviceProvider.GetKeyedService<IServiceA>("A1");
obj.Say();

obj = serviceProvider.GetKeyedService<IServiceA>("A2");
obj.Say();
}

参数Key是一个object类型,所以也可以是其他类型作为 Key。

#生命周期

#Singleton

  • 单例。每次请求都会返回相同的实例。ServiceProvider会引用该实例。
1
2
3
4
5
6
7
8
9
10
11
12
private static void Foo()
{
var serviceProvider = new ServiceCollection()
.AddSingleton<IService, Service>()
.BuildServiceProvider();

var service = serviceProvider.GetService<IService>();
Debug.WriteLine($"{service.GetHashCode()}"); // 4094363

service = serviceProvider.GetService<IService>();
Debug.WriteLine($"{service.GetHashCode()}"); // 4094363
}

两次输出相同的 HashCode。

  • 支持从已有实例进行注册
1
AddSingleton(new Service());

#Scoped

  • 作用域。在同一个作用域内请求会返回相同的实例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static void Foo()
{
var serviceProvider = new ServiceCollection()
.AddScoped<IService, Service>()
.BuildServiceProvider();

using (var scope = serviceProvider.CreateScope())
{
var s1 = scope.ServiceProvider.GetService<IService>();
Debug.WriteLine($"{s1.GetHashCode()}"); // 4094363

var s2 = scope.ServiceProvider.GetService<IService>();
Debug.WriteLine($"{s2.GetHashCode()}"); // 4094363
}

using (var scope = serviceProvider.CreateScope())
{
var s1 = scope.ServiceProvider.GetService<IService>();
Debug.WriteLine($"{s1.GetHashCode()}"); // 41962596

var s2 = scope.ServiceProvider.GetService<IService>();
Debug.WriteLine($"{s2.GetHashCode()}"); // 41962596
}
}

如果不在作用域内请求也是可以的,但是会产生单例的效果:

1
2
3
4
5
6
7
8
9
10
11
12
private static void Foo()
{
var serviceProvider = new ServiceCollection()
.AddScoped<IService, Service>()
.BuildServiceProvider();

var s3 = serviceProvider.GetService<IService>();
Debug.WriteLine($"{s3.GetHashCode()}"); // 42119052

var s4 = serviceProvider.GetService<IService>();
Debug.WriteLine($"{s4.GetHashCode()}"); // 42119052
}

在作用域外请求被认为是反模式,为了约束这种错误的用法,可以在BuildServiceProvider方法中传入validateScopes = true

1
2
3
4
5
6
7
8
private static void Foo()
{
var serviceProvider = new ServiceCollection()
.AddScoped<IService, Service>()
.BuildServiceProvider(validateScopes: true);

var s3 = serviceProvider.GetService<IService>(); // InvalidOperationException
}

错误的用法将会抛出一个InvalidOperationException异常。

#Transient

  • 瞬态。每次请求都创建一个新的实例。
1
2
3
4
5
6
7
8
9
10
11
12
private static void Foo()
{
var serviceProvider = new ServiceCollection()
.AddTransient<IServiceA, ServiceA>()
.BuildServiceProvider();

var s1 = serviceProvider.GetService<IServiceA>();
Debug.WriteLine($"{s1.GetHashCode()}"); // 4094363

var s2 = serviceProvider.GetService<IServiceA>();
Debug.WriteLine($"{s2.GetHashCode()}"); // 19575591
}

每次请求都是新的实例。

#注入

Microsoft.Extensions.DependencyInjection仅有构造函数注入。并不支持属性注入。

#构造注入

  • 容器会选择具有最多参数的构造函数来进行注入。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
AddTransient<IServiceA, ServiceA>();
AddTransient<IServiceB, ServiceB>();

public class ServiceC : IServiceC
{
public ServiceC(IServiceA a)
{
Debug.WriteLine("1");
}

public ServiceC(IServiceA a, IServiceB b) // 命中
{
Debug.WriteLine("2");
}

public ServiceC(int n1, int n2, int n3) // 参数虽多,但是类型没有注册
{}
}

#总结

Microsoft.Extensions.DependencyInjection很轻量,它并不是一个大而全的 IoC 容器。对于复杂的依赖注入场景来说功能有限。为了满足更高级的需求,可以使用其他第三方容器。
它只是微软官方一系列扩展包中的一个公共模块,是为了服务其他扩展的。

以下是其他部分扩展:

  1. Microsoft.Extensions.Logging:提供了日志记录功能的扩展,可以与各种日志记录器(如Console、Debug、File、EventSource等)集成。
  2. Microsoft.Extensions.Configuration:提供了配置管理功能的扩展,可以从不同的配置源(如JSON、XML、环境变量等)读取和解析配置信息。
  3. Microsoft.Extensions.Caching:提供了缓存功能的扩展,可以将数据缓存在内存、分布式缓存(如Redis)中,并提供灵活的缓存策略。
  4. Microsoft.Extensions.Options:提供了配置选项功能的扩展,可以轻松地将配置选项注入到应用程序中,并进行验证和转换。
  5. Microsoft.Extensions.Diagnostics.HealthChecks:提供了应用程序健康检查功能的扩展,可以检查应用程序的各种依赖和状态。


官方的扩展远不止这几个,更多的可以直接去 nuget 网站搜索前缀Microsoft.Extensions即可。便民链接
这些包的设计目标是提供可插拔的模块,以便开发人员根据项目需求选择适当的功能和扩展。

#参考

Exploring the Microsoft.Extensions.DependencyInjection Machinery