Part2 依赖注入
概念
- 实现控制反转的两种方式
- 服务定位(ServiceLocator)
- 依赖注入(DependencyInjection)
- 服务: 对象
- 服务容器: 负责管理注册的服务
- 查询服务: 创建对象及关联对象
- 对象生命周期:
- 瞬态: Transient
- 范围: Scoped
- 单例: Singleton
- 根据类型来获取和注册服务
- 可以分别指定服务类型和实现类型
- 服务类型可以是类/接口
生命周期
- 使用 serviceProvider.CreateScope() 来创建 Scope
- 如果一个类实现了 IDisposable 接口, 在离开作用域之后容器会自动调用对象的 Dispose 方法
- 不要在长生命周期的对象中引用比它短的生命周期的对象(ASP.NET Core 中默认抛异常)
- 生命周期的选择
- 如果类无状态, 建议为 Singleton
- 如果类有状态, 且有 Scope 控制, 建议为 Scoped, 通常 Scope 控制下的代码都是运行在同一个线程中的, 没有并发修改的
- 问题
/*
* DI测试1
*/
// services.AddTransient<TestService>();
// services.AddSingleton<TestService>();
services.AddScoped<TestService>();
using var serviceProvider = services.BuildServiceProvider();
var testService = serviceProvider.GetService<TestService>();
var testService2 = serviceProvider.GetService<TestService>();
// 如果是瞬态, 返回False, 如果是单例或范围, 返回True
Console.WriteLine(ReferenceEquals(testService, testService2));
/*
* DI测试2 Scope
*/
TestService? testService3;
TestService? testService4;
using (var scope = serviceProvider.CreateScope())
{
testService3 = scope.ServiceProvider.GetService<TestService>();
testService4 = scope.ServiceProvider.GetService<TestService>();
// True
Console.WriteLine(ReferenceEquals(testService3, testService4));
}
using (var scope2 = serviceProvider.CreateScope())
{
var testService5 = scope2.ServiceProvider.GetService<TestService>();
// False
Console.WriteLine(ReferenceEquals(testService3, testService5));
}
服务定位器 IServiceProvider
services.AddScoped<ITestService, TestService>();
services.AddScoped<ITestService, TestService2>();
using var serviceProvider = services.BuildServiceProvider();
// GetService() 如果没有注入, 则返回null
var testService = serviceProvider.GetService<ITestService>();
// GetRequiredService() 如果没有注入, 会抛异常
var testService2 = serviceProvider.GetRequiredService<ITestService>();
// GetServices() 获取多个实现服务
var testServices = serviceProvider.GetServices<ITestService>();
foreach (var item in testServices)
{
Console.WriteLine(item.GetType().Name);
}
依赖注入