.Net Core 依賴注入(IOC) 一些簡單的使用技巧

原文鏈接://www.cnblogs.com/ysmc/p/16240534.html

  .Net Core 在使用IOC後,我們不必再浪費精力在管理實例的生命周期上,交給IOC代替我們管理,減少我們成噸的代碼,面向接口編程更是靈活到了極致,而IOC的三種生命周期應該怎麼去使用呢,Transient(瞬態)、Scoped(作用域)、Singleton(單例)。

Transient(瞬態)

  這個沒什麼好說的,就是每次注入的時候,容器自動 new 一個實例,用完就丟;

Scoped(作用域)

  以Web來說,作用域的生命周期就是當次請求,請求開始後的第一次注入,就是它生命的開始,直到請求結束;

  我個人常用來減少數據獲取,提升請求響應,舉一個例子:A服務是獲取全國地級市信息的,以作用域的方式註冊到IOC容器中,B、C、D 都注入了A服務並使用了它;一個業務接口,剛好涉及到了B、C、D,當接口被調用,代碼執行到了B,第一次調用了 A 服務請求數據庫獲取了全國地級市數據;然後執行到了C,又一次使用了A服務獲取了數據,最後D;一個請求下來,A被使用了3次,獲取了3個同樣的數據結果,這完全是在浪費資源;

 1 public class AService
 2 {
 3     public async CityData GetCityDataAsync()
 4     {
 5         return await GetDatasFromApi();
 6     }
 7 }
 8 
 9 public class BService
10 {
11     private readonly AService _aService;
12 
13     public BService(AService aService)
14     {
15         _aService = aService;
16     }
17     
18     public async Task Execute()
19     {
20         await _aService.GetCityDataAsync();
21     }
22 }
23 
24 public class CService
25 {
26     private readonly AService _aService;
27 
28     public CService(AService aService)
29     {
30         _aService = aService;
31     }
32 
33     public async Task Execute()
34     {
35         await _aService.GetCityDataAsync();
36     }
37 }
38 
39 public class DService
40 {
41     private readonly AService _aService;
42 
43     public DService(AService aService)
44     {
45         _aService = aService;
46     }
47 
48     public async Task Execute()
49     {
50         await _aService.GetCityDataAsync();
51     }
52 }

  那我們應該怎麼做呢,首先我們回顧一下 Scoped 的生命周期,可以說是一個實例,貫穿了整一個請求,那我們是不是可以定義一個變量,請求數據前先判斷這個變量有沒有值,沒有就去獲取數據,給它賦值,下次再調用的時候,直接返回這個變量的數據,這樣不管這個服務被調用多少次,它也只是調用了一次數據庫,大大節省了資源。

 1 public class AService
 2 {
 3     private List<CityData> cityDatas;
 4         
 5     public async List<CityData> GetCityDataAsync()
 6     {
 7         if(cityDatas == null|| !cityDatas.Any())
 8         {
 9             cityDatas = await GetDatasFromApi();
10         }
11         
12         return cityDatas;
13     }
14 }

  有人可能說會說了,不就是多調用幾次數據庫,現在服務器的性能這麼好,不在乎這一點的資源;確實,如果只是多調用幾次數據庫,對於一些小系統來說,跟撓痒痒一樣,那這裡的調用數據庫換成調用 WebApi 呢?如果還是調用第三方的 API 呢?一次Http請求,不往大的說,從請求到獲取到數據,花個100ms很正常吧(網絡非常好的情況當我沒說),那這個接口不需要多,調用10次就1s了,還沒算上其它業務邏輯的耗時呢,如果還需要調用其它的api,那響應時間就更長咯,難道你讓用戶打開一個頁面,或者點擊一個按鈕,需要等上兩三秒才有響應嗎,這樣的用戶體驗就非常糟糕了。

Singleton(單例)

  來自依賴關係注入容器的服務實現的每一個後續請求都使用同一個實例。 如果應用需要單一實例行為,則允許服務容器管理服務的生存期。必須是線程安全的,並且通常在無狀態服務中使用。

  在單例中,不要直接注入作用域的服務,這會引起很多莫名其妙的錯誤,一定要使用的話,就自己創建,自己管理它的生命周期:

public class Scope
{
    private readonly IServiceScopeFactory _serviceScopeFactory;
public Scope(IServiceScopeFactory serviceScopeFactory) { _serviceScopeFactory = serviceScopeFactory; } public async Task CreateScope() { using var scope = _serviceScopeFactory.CreateScope(); var service = scope.ServiceProvider.GetRequiredService<IService>(); } }

 ActivatorUtilities

  有些情況下,例如當你不想把使用次數極低的類註冊到容器中,或者這個類的構造函數需要傳入一些參數,但是又需要用到容器中的服務的時候,你可以使用 ActivatorUtilities 中的 CreateInstance 去創建它,它會自動給構造函數注入所需的服務,並且還可以給構造函數傳參,滿足上面所說情況的需求。

 1 public class TestTask : ITask
 2 {
 3     private readonly IServiceScopeFactory _serviceScopeFactory;
 4 
 5     private readonly IRabbitMQService _rabbitMQService;
 6 
 7     private readonly string _name;
 8 
 9     public TestTask(IServiceScopeFactory serviceScopeFactory, IRabbitMQService rabbitMQService, string name)
10     {
11         _serviceScopeFactory = serviceScopeFactory;
12         _rabbitMQService = rabbitMQService;
13         _name = name;
14     }
15 
16     /// <summary>
17     /// 
18     /// </summary>
19     /// <param name="cancellationToken"></param>
20     /// <returns></returns>
21     /// <exception cref="NotImplementedException"></exception>
22     public async Task Execute(CancellationToken cancellationToken)
23     {
24         try
25         {
26             using var scope = _serviceScopeFactory.CreateScope();
27 
28             var executionService = scope.ServiceProvider.GetService<ITaskExecutionService>();
29 
30             if (executionService != null)
31             {
32                 await _rabbitMQService.RabbitMQReceiveService.SingleAsync(executionService.ExecuteAsync);
33             }
34         }
35         catch (Exception err)
36         {
37 
38             Console.WriteLine(err.Message);
39         }
40     }
41 }

  使用 ActivatorUtilities 創建:

1 var testTask = ActivatorUtilities.CreateInstance<TestTask>(serviceProvider, "test");
2 
3 await testTask.Execute(new CancellationToken());

寫在最後

Bootstrap Blazor 官網地址://www.blazor.zone

  希望大佬們看到這篇文章,能給項目點個star支持下,感謝各位!

star流程:

1、訪問點擊項目鏈接:BootstrapBlazor   star

2、點擊star,如下圖,即可完成star,關注項目不迷路:

 

另外還有兩個GVP項目,大佬們方便的話也點下star唄,非常感謝:

  BootstrapAdmin 項目地址:star
  //gitee.com/LongbowEnterprise/BootstrapAdmin

  SliderCaptcha 項目地址:star
  //gitee.com/LongbowEnterprise/SliderCaptcha

 

交流群(QQ)歡迎加群討論

       BA & Blazor ①(795206915)          BA & Blazor ②(675147445)