分享翻譯一篇Abp框架做者(Halil İbrahim Kalkan)關於ASP.NET Core依賴注入的博文.git
在本文中,我將分享我在ASP.NET Core應用程序中使用依賴注入的經驗和建議.github
這些原則背後的目的是:c#
本文假設你已經熟悉基本的ASP.NET Core以及依賴注入. 若是沒有的話,請首先閱讀ASP.NET核心依賴注入文檔.
ASP.NET Core 依賴注入文檔緩存
構造函數注入用在服務的構造函數上聲明和獲取依賴服務.
例如:安全
public class ProductService { private readonly IProductRepository _productRepository; public ProductService(IProductRepository productRepository) { _productRepository = productRepository; } public void Delete(int id) { _productRepository.Delete(id); } }
ProductService在構造函數中將IProductRepository注入爲依賴項,而後在Delete方法中使用它.多線程
ASP.NET Core的標準依賴注入容器不支持屬性注入,可是你可使用其它支持屬性注入的IOC容器.
例如:框架
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; namespace MyApp { public class ProductService { public ILogger<ProductService> Logger { get; set; } private readonly IProductRepository _productRepository; public ProductService(IProductRepository productRepository) { _productRepository = productRepository; Logger = NullLogger<ProductService>.Instance; } public void Delete(int id) { _productRepository.Delete(id); Logger.LogInformation( $"Deleted a product with id = {id}"); } } }
ProductService具備公開的Logger屬性. 依賴注入容器能夠自動設置Logger(前提是ILogger以前註冊到DI容器中).ide
Logger = NullLogger<ProductService>.Instance;
), 否則就須要在使用依賴項時始終作空引用的檢查.服務定位器模式是獲取依賴服務的另外一種方式.
例如:函數
public class ProductService { private readonly IProductRepository _productRepository; private readonly ILogger<ProductService> _logger; public ProductService(IServiceProvider serviceProvider) { _productRepository = serviceProvider .GetRequiredService<IProductRepository>(); _logger = serviceProvider .GetService<ILogger<ProductService>>() ?? NullLogger<ProductService>.Instance; } public void Delete(int id) { _productRepository.Delete(id); _logger.LogInformation($"Deleted a product with id = {id}"); } }
ProductService服務注入IServiceProvider並使用它來解析其依賴,若是欲解析的依賴未註冊GetRequiredService會拋出異常,GetService只返回NULL.單元測試
在構造函數中解析的依賴,它們將會在服務被釋放的時候釋放,所以你不須要關心在構造函數中解析的服務釋放/處置(release/dispose),這點一樣適用於構造函數注入和屬性注入.
ASP.NET Core下依賴注入中有三種服務生命週期:
DI容器自動跟蹤全部已解析的服務,服務在其生命週期結束時被釋放/處置(release/dispose)
在某些狀況下你可能須要在服務方法中解析其餘服務.在這種狀況下,請確保在使用後及時釋放解析得服務,確保這一點的最佳方法是建立Scoped服務.
例如:
public class PriceCalculator { private readonly IServiceProvider _serviceProvider; public PriceCalculator(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public float Calculate(Product product, int count, Type taxStrategyServiceType) { using (var scope = _serviceProvider.CreateScope()) { var taxStrategy = (ITaxStrategy)scope.ServiceProvider .GetRequiredService(taxStrategyServiceType); var price = product.Price * count; return price + taxStrategy.CalculateTax(price); } } }
PriceCalculator在構造函數中注入IServiceProvider服務,並賦值給_serviceProvider屬性. 而後在PriceCalculator的Calculate方法中使用它來建立子服務範圍。 它使用scope.ServiceProvider來解析服務,而不是注入的_serviceProvider實例。 所以從範圍中解析的全部服務都將在using語句的末尾自動釋放/處置(release/dispose)
單例服務一般用於保持應用程序狀態. 緩存服務是應用程序狀態的一個很好的例子.
例如:
public class FileService { private readonly ConcurrentDictionary<string, byte[]> _cache; public FileService() { _cache = new ConcurrentDictionary<string, byte[]>(); } public byte[] GetFileContent(string filePath) { return _cache.GetOrAdd(filePath, _ => { return File.ReadAllBytes(filePath); }); } }
FileService緩存文件內容以減小磁盤讀取. 此服務應註冊爲Singleton,不然緩存將沒法按預期工做.
Scoped生命週期的服務乍一看彷佛是存儲每一個Web請求數據的良好候選者.由於ASP.NET Core會爲每一個Web請求建立一個服務範圍. 所以,若是你將服務註冊爲做用域則能夠在Web請求期間共享該服務.
例如:
public class RequestItemsService { private readonly Dictionary<string, object> _items; public RequestItemsService() { _items = new Dictionary<string, object>(); } public void Set(string name, object value) { _items[name] = value; } public object Get(string name) { return _items[name]; } }
若是將RequestItemsService註冊爲Scoped並將其注入兩個不一樣的服務,則能夠獲取從另外一個服務添加的項,由於它們將共享相同的RequestItemsService實例.這就是咱們對Scoped生命週期服務的指望.
可是...事實可能並不老是那樣. 若是你建立子服務範圍並從子範圍解析RequestItemsService,那麼你將得到RequestItemsService的新實例,它將沒法按預期工做.所以,做用域服務並不老是表示每一個Web請求的實例。
你可能認爲你沒有犯這樣一個明顯的錯誤(在子範圍內解析服務). 狀況可能不那麼簡單. 若是你的服務之間存在大的依賴關係,則沒法知道是否有人建立了子範圍並解析了注入另外一個服務的服務.最終注入了做用域服務.
依賴注入起初看起來很簡單,可是若是你不遵循一些嚴格的原則,就會存在潛在的多線程和內存泄漏問題. 我根據本身在ASP.NET Boilerplate框架開發過程當中的經驗分享了一些很好的原則.
原文地址:ASP.NET Core Dependency Injection Best Practices, Tips & Tricks