.NET依赖注入
概念
服务:对象。
服务容器:负责注册服务和管理服务的容器。
查询服务:创建对象及关联对象。
对象生命周期:Transient(瞬态);Scoped(范围);Singleton(单例)。
.NET 中使用 DI
根据类型来获取和注册服务。可以分别指定服务类型和实现类型。这两者可能相同,也可能不同。服务类型可以是类,也可以是接口,建议面向接口编程,会更加灵活。
- 安装
Install-Package Microsoft.Extensions.DependencyInjection
- 引入命名空间
using Microsoft.Extensions.DependencyInjection;
- 使用流程
SerivceCollection 注册服务-》创建 ServiceProvider-》向 ServiceProvider 获取服务。
基本使用:
ServiceCollection services = new ServiceCollection();
services.AddScoped<Class1>();
using (ServiceProvider sp =services.BuildServiceProvider())
{
Class1 c =sp.GetService<Class1>();
c.Name = "Test";
c.SayHi();
}
超出using范围后,sp会被销毁。但是以已经通过sp获取到的服务还是可用的。
没添加scope时,AddScoped和AddSingleton效果一样(多次获取的服务可以用object.Euqals验证,是同一个对象)。不写Scope,默认有一个全局的Scoped
生命周期
- 使用serviceProvider。CreateScope()创建scope
ServiceCollection services = new ServiceCollection();
services.AddScoped<Class1>();
;
using (ServiceProvider sp =services.BuildServiceProvider())
{
using (IServiceScope scope1 = sp.CreateScope())
{
//在scope中使用sp时,不能直接去用外层的sp,而是通过scope1.ServiceProvider 去取
scope1.ServiceProvider.GetService<Class1>();
}
}
在scope中使用sp时,不能直接去用外层的sp,而是通过scope1.ServiceProvider 去取
不同scope中获取的服务不是同一个对象,同一个scope中获取的服务是同一个对象。
- 如果一个服务类实现了IDisposable,那么在scope结束时,会自动调用Dispose销毁对象。因此不要在scope外部引用scope内部获取的服务。//TODO:Dispose实现需要做哪些工作
- 不要在长生命周期的对象中引用比他短的对象,否则会直接报异常。例如 Singleton 引用 Transient等
- 生命周期选择:类无状态,没有什么并发问题,建议为Singleton,如果类有状态,且有Scoped控制,建议为Scoped;使用Transient要谨慎。
- 注册服务的重载方法有很多。
服务定位器
服务类型与实现类型不一致的用法:
ServiceCollection services = new ServiceCollection();
services.AddScoped<Interface1, Class1>();//这一步目前自己注册,其他框架可能会自己注册,注册服务和使用服务不是同一个人
;
using (ServiceProvider sp = services.BuildServiceProvider())
{
Interface1 i1 = sp.GetService<Interface1>();
i1.Name = "A";
i1.SayHi();
Console.WriteLine(i1.GetType());
}
目前自己注册,自己获取服务。后续使用其他框架时,可能会框架自己注册,写代码时只用获取服务就行。
需要构造入参的类如何注册服务:
services.AddSingleton(typeof(Interface1), new Class1(123));
GetService 方法获取不到对象时,返回null;GetRequiredService 方法获取不到对象时,会抛出异常。IEnumerable
依赖注入
-
依赖注入是有“传染性”的,如果一个类的对象是通过DI创建的,那么这个类的构造函数中声明的所有服务类型的参数都会被DI赋值;但是如果一个对象是程序员手动创建的,那么这个对象就和DI没有关系,它的构造函数中声明的服务类型参数就不会被自动赋值。
-
.NET的DI默认是构造函数注入。