目前為止設定 DI 都是按照官方的文件設定一下,就放著不理他了。 結果今天踩到一個超級大雷,所以我要鄭重地說:

DI 的三種生命週期很重要!! 不要亂設定!!

前提概要

因為我最近都在整合 PayPal 的服務,所以很常要寫程式發 http request,而我用的則是 RestSharp 這個套件,他是一個只要設定網址、header、body等等…就能夠很輕易地發送 http request

根據 PayPal API 的規範,在使用付款或者建立發票等等的 API ,要先去呼叫 OAuth API 取的 token。

所以我程式就這樣設計

  • 使用 RestSharp 套件的 RestClient 發送 http request
  • PayPalService 使用 RestClient 呼叫 OAuth API 取得 token。
  • InvoiceService 使用 RestClient 呼叫發票相關 API 建立發票,而在呼叫建立發票 request 的時後,要傳入 token。

程式範例

為了讓程式能夠更容易了解,我有將原本的程式碼簡化,而這三個類別程式碼如下

PayPalService 與 InvoiceService 都是呼叫 restClient.Url 設定網址,接下來就是發送 request

code
  • RestClient
  • PayPalService
  • InvoiceService
1
2
3
4
5
6
7
8
9
public class RestClient : IRestClient
{
public string Url { get; set; }

public string SendRequest(RestRequest request)
{
return $"I use this url:{Url} send request";
}
}

接下來我有一個 ValuesController 建構式注入 IInvoiceService, 並且有一個建立發票的API。

ValuesController
  • cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class ValuesController : ApiController
{
private IInvoiceService _invoiceService;
public ValuesController(IInvoiceService invoiceService)
{
this._invoiceService = invoiceService;
}

[HttpGet]
public IHttpActionResult CreateInvoice()
{
this._invoiceService.CreateInvoice();

return Ok();
}
}

程式碼跑執行之網址改變了

Url 改變了 !!Url 改變了 !!

為什麼會這樣

因為我 DI 注入的時候使用 InstancePerLifetimeScope 的生命週期

意思是說,只要是同一個 request 就會使用同一個物件 !!!

PayPalService 與 InvoiceService 使用的是同一個 RestClient 物件

所以在取得 Token 的時候,把 InvoiceService 設定好的 Url 覆寫掉了

DI 三種生命週期

  • SingleInstance : 整個應用程式都是使用同一個物件
  • InstancePerLifetimeScope : 只要是同一個 request 則使用同一個物件
  • InstancePerDependency : 只要有注入都是一個全新的物件

所以我只要將 RestClient 注入的生命週期改成 InstancePerDependency,問題就會解決了。

執行結果

Url 沒有改變 !!Url 沒有改變 !!

參考

docs.autofac.org