一、原理和注意事項:
作用域由 IServiceScope 對象承載,對於實現了IDisposable接口類型的對象,容器會去負責對其生命週期的管理,當使用完畢它會去釋放對象;實現IDisposable接口類型對象的釋放特點:
- DI 只負責釋放由其創建的對象;
- DI 在容器或子容器釋放時,釋放由其創建的對象實例;
如果對象是由我們自己創建並放到容器裏面,容器不負責釋放該對象;容器的生命週期與其創建的對象的生命週期有對應關係,這裏我們需要以下兩點注意事項:
- 在根容器不要去創建實現了 IDisposable 的瞬時服務;
- 避免手動創建對象且將該對象放入容器裏面,應該儘可能的使用容器去管理我們的對象的創建和釋放;
二、代碼示例與測試結果:
首先創建一個 OrderService 的服務,代碼如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
namespace DependencyInjectionScopeAndDisposableDemo.Services
{
public interface IOrderService
{
}
public class DisposableOrderService : IOrderService, IDisposable
{
public void Dispose()
{
Console.WriteLine($"DisposableOrderService Disposed:{this.GetHashCode()}");
}
}
}
然後在 Startup 的 ConfigureServices 中分別註冊以下服務,測試作用域對象的生命週期和釋放,如下所示:
//1.瞬態
services.AddTransient<IOrderService, DisposableOrderService>();
services.AddTransient<IOrderService>(service => new DisposableOrderService()); //工廠模式註冊;
//2.單例
services.AddSingleton<IOrderService, DisposableOrderService>();
services.AddSingleton<IOrderService>(services => new DisposableOrderService());
//3.作用域
services.AddScoped<IOrderService, DisposableOrderService>();
services.AddScoped<IOrderService>(services => new DisposableOrderService());
//4.自創建方式單例註冊
var service = new DisposableOrderService();
services.AddSingleton<IOrderService>(service);
測試輸出對象的HashCode和該對象的生命週期和釋放行爲,控制器方法如下:
- 瞬態服務註冊測試:
[HttpGet]
public int Get(
[FromServices]IOrderService orderService,
[FromServices]IOrderService orderService2)
{
Console.WriteLine("接口請求處理結束");
return 1;
}
1.瞬態測試輸出結果如下:
接口請求處理結束
DisposableOrderService Disposed:21621962
DisposableOrderService Disposed:29422698
瞬時服務在每一接口調用時都會獲得一個對象,且在接口處理完畢後將其註冊對象服務釋放;
- 單例服務註冊測試:
2.單例測試,控制器中的請求方法不變(如下3),測試結果如下:
=======1==========
=======2==========
接口請求處理結束
執行順序一樣,但單例模式註冊服務的對象不會被釋放;
- 作用域服務註冊測試:
[HttpGet]
public int Get(
[FromServices]IOrderService orderService,
[FromServices]IOrderService orderService2)
{
#region 測試作用域Scope
Console.WriteLine("=======1==========");
using (IServiceScope scope = HttpContext.RequestServices.CreateScope())
{
//創建作用域子容器
var service = scope.ServiceProvider.GetService<IOrderService>();
var service2 = scope.ServiceProvider.GetService<IOrderService>();
}
Console.WriteLine("=======2==========");
#endregion
Console.WriteLine("接口請求處理結束");
return 1;
}
3.作用域測試輸出結果如下:
=======1==========
DisposableOrderService Disposed:8140213
=======2==========
接口請求處理結束
DisposableOrderService Disposed:21353980
同樣作用域服務註冊還是先執行接口方法,執行完畢再將其對象釋放,但同一個作用域內對象只會創建一次(局部單例模式);
- 自創建方式單例註冊
4.測試結果如下:
=======1==========
=======2==========
接口請求處理結束
同樣的測試結果,自創建註冊方式,對象不會被其容器所釋放;
以上就是對服務註冊的幾種方式以及對象釋放的生命週期測試結果;