.Net Core 依賴注入
- 為什麼要使用依賴注入架構
- .Net Core DI
-
- 核心類
- 三種生命周期
- 服務注冊
-
- 單例注冊
- 作用域注冊
- 瞬時注冊
- 直接注入執行個體
- 工廠模式注冊
- 注冊不同執行個體
- 嘗試注冊
- 移除和替換注冊
- 注冊泛型模闆
- 使用依賴注入注意點
- 實作 IDisposable 接口類型的釋放
- Autofac
-
- 基于名稱的注入
- 屬性注入
- 子容器
- 基于動态代理的 AOP
為什麼要使用依賴注入架構
- 借助依賴注入架構,将對象的建立交由容器管理,確定代碼的可維護性和可擴充性。
- .NET Core 的整個架構中,依賴注入架構提供了對象建立和生命周期管理的核心能力,各個元件互相協作,也是由依賴注入架構的能力來實作的。
.Net Core DI
.Net Core實作依賴注入兩個包:
Microsoft.Extensions.DependencyInjection.Abstractions
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection.Abstractions 為抽象包,Microsoft.Extensions.DependencyInjection 為具體實作包
核心類
IServiceCollection
ServiceDescriptor
IServiceProvider
IServiceScope
IServiceCollection 負責注冊,作為Startup類中ConfigureServices方法的參數,使用其對對象進行注冊。
ServiceDescriptor 負責注入服務元素的描述
IServiceProvider 負責提供執行個體
IServiceScope 負責執行個體的生命周期
三種生命周期
- 瞬時 Transient:每次請求,都擷取一個新的執行個體;即使同一個請求擷取多次也會是不同的執行個體。
- 作用域 Scoped: 每次請求,都擷取一個新的執行個體;同一個請求擷取多次會得到相同的執行個體,即一個http請求内唯一。
- 單例 Singleton:每次都擷取同一個執行個體。
服務注冊
單例注冊
作用域注冊
瞬時注冊
直接注入執行個體
工廠模式注冊
services.AddScoped<IMyService>(serviceProvider =>
{
return new MyService();
});
注冊不同執行個體
services.AddScoped<IMyService>(new MyService());
services.AddScoped<IMyService>(new MyService2());
MyService和MyService2均實作了IMyService接口,并且進行了注冊;使用的時候,預設是用的最後注冊的(MyService2);如果要使用MyService, 需要周遊,根據實際類型來判斷。
[HttpGet]
public async Tash GetServiceList([FromServices] IEnumerable<IMyService> services)
{
foreach (var service in services)
{
if (service is MyService) // if (service.GetType() == typeof(MyService))
{
// do sth
}
}
}
嘗試注冊
services.TryAddScoped<IMyService>(new MyService());
services.TryAddEnumerable(ServiceDescriptor.Scoped<IMyService, MyService>());
嘗試注冊會先判斷執行個體是否已經注冊,若注冊了則不再進行注冊;嘗試注冊可以有效的避免服務重複注冊。
移除和替換注冊
services.AddScoped<IMyService, MyService>();
services.Replace(ServiceDescriptor.Singleton<IMyService, MyService2>());
services.RemoveAll<IMyService>();
替換注冊可以替換原有執行個體注冊MyService為MyService2,RemoveAll則是移除是以實作了IMyService的注冊執行個體。
注冊泛型模闆
使用依賴注入注意點
- 避免通過靜态屬性的方式通路容器對象
- 避免在服務内部使用 GetService 方式來擷取執行個體
- 避免使用靜态屬性存儲單例,應該使用容器管理單例對象
- 避免在服務中執行個體化依賴對象,應該使用依賴注入來獲得依賴對象
- 避免向單例的類型注入範圍的類型
實作 IDisposable 接口類型的釋放
- DI 隻負責釋放由其建立的對象執行個體
- DI 在容器或子容器釋放時,釋放由其建立的對象執行個體
- 避免手動建立實作了 IDisposable 對象,應該使用容器來管理其生命周期
Autofac
Autofac的功能比.Net Core 的原生依賴注入更豐富,具體表現在:
- 基于名稱的注入
- 屬性注入
- 子容器
- 基于動态代理的 AOP
基于名稱的注入
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<MyService>().Named<IMyService>("s1");
builder.RegisterType<MyService2>().Named<IMyService>("s1");
}
從IoC容器中使用ResolveNamed擷取:
var s1 = container.ResolveNamed<IMyService>("s1");
var s2 = container.ResolveNamed<IMyService>("s2");
屬性注入
使用PropertiesAutowired方法支援屬性注入:
子容器
使用
var autofacContainer = app.ApplicationServices.GetAutofacRoot();
using (var myscope = autofacContainer.BeginLifetimeScope("myscope"))
{
var service1 = myscope.Resolve<MyService>();
using (var scope = myscope.BeginLifetimeScope())
{
var service2 = scope.Resolve<MyService>();
Console.WriteLine($"service1=service2:{service1 == service2}");
}
}
基于動态代理的 AOP
需要引用包:Autofac.Extras.DynamicProxy
先實作一個攔截器繼承 IInterceptor
public class MyInterceptor : IInterceptor
{
public void Intercept(IInvocation invocation)
{
Console.WriteLine($"Intercept before,Method:{invocation.Method.Name}");
invocation.Proceed();
Console.WriteLine($"Intercept after,Method:{invocation.Method.Name}");
}
}
注冊
這裡實作了基于接口的攔截,也可以實作基于類的攔截;基于類的攔截時,方法需要定義為虛方法。