分享翻译一篇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