.NET CORE 依赖注入 实践总结
知识点回顾
- 依赖包。 Microsoft.Extensions.DependencyInjection.Abstractions
- 核心对象和方法。
- IServiceCollection。注入对象的容器。注意它只存储对象的元数据,并不保存实例对象。
- IServiceProvider。注入对象的提供者。它负责提供具体的对象实例。在架构中,IServiceProvider有2种身份,一种是Root ServiceProvider,由service.BuildServiceProvider()创建,生命周期贯穿整个应用程序,AddSingleton对象保存在这里。另外一种则是普通IServiceProvider,由IServiceScope创建,生命周期即为AddScoped的生命周期。AddScope 的对象保存在这里。普通ServiceProvider由Root ServiceProvider创建的IServiceScope创建。
- IServiceScope。表某一个生命周期范围。由ServiceProvider.CreateScope()创建。
- 注入方式,知识点一。
- 注入功能默认在Startup类中的ConfigureServices方法中。
- 注入方式,知识点二。支持以下三种方式
- AddScoped。生命周期为Scoped类型。例如在web框架中,即表示一次Request请求范围内。
- AddSingleton。单例,应用程序全局使用同一个实例。
- AddTransient。即时的,即对象每次使用都会重新实例化。
- 注入方式,知识点三。提供多种注入技巧,以Transient为例
- 实例注入。AddTransient<TService>(this IServiceCollection services)。
- 泛型注入。AddTransient<TService, TImplementation>(this IServiceCollection services)。
- 工厂注入。AddTransient<TService>(this IServiceCollection services, Func<IServiceProvider, TService> implementationFactory)。
- TryAddXXX。仅当XXX尚未注册实现时,注册该服务。此方法用来避免在容器中注册一个实例的两个副本。
- 获取实例的方法 GetService和GetRequiredService的区别,前置如果service不存在会返回NULL,后者会直接抛出异常。根据需要选择GetRequiredService,可能会让你的代码变得简洁一点。
WHY 依赖注入
这里只谈益处。
- 使用接口或基类抽象化依赖关系实现,明确各个类之间的依赖关系。
- 生命周期的统一管理,尤其对于某些类被多处依赖,关系会变得分散难以管理,依赖注入容器可以解决这点。
- 非常利于单元测试。
最佳实践
部分来自官方文档的一些建议
- 对于需要注入为单例的实例,不要依赖Scoped实例。会触发 .NET CORE作用域验证失败。
- 不要从Root IServiceProvider解析有作用域的实例,这样会导致该作用域的实例变成单一实例。同样会触发作用域验证失败。
- 对于Asp.Net Core,尽量通过构造函数而不是HttpContext.RequestServices获取实例,这样更易于单元测试。
- 需要对某个组件服务或是一些服务集合(包括其依赖注入时),使用约定的 Add{SERVICE_NAME} 扩展方法来注册该服务所需的所有服务。
- 若必须要从IServiceProvider中解析实例(如在单元测试中),请通过using (var scope = ServiceProvider.CreateScope()){ }方式创建作用域来获取实例。
- 代码中避免设计有状态的、静态类和成员。可以考虑设计注入为单一实例。
- 代码中避免在服务中直接实例化以来类。尽量采用依赖注入的方式
- 注意以下两种方式注入的区别,后者的实例化不是服务容器创建的,所以容器不会处理实例的Dispose !!
public class Service1 : IDisposable {}
public class Service2 : IDisposable {} //方式一
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Service1>();
services.AddSingleton<IService2>(sp => new Service2());
} //方式二
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Service1>(new Service1());
services.AddSingleton(new Service2());
} - 延伸上一点,对于复杂对象的创建,尽量采用提供的工厂注入方式。注意工厂注入的参数是IServiceProvider,可以通过它获取你需要的实例对象。
- 继续延伸上一点,不要在ConfigureServices方法中 通过collection.BuildServiceProvider()来获取IServiceProvider。这个创建的是一个Root IServiceProvider。单例会实例化一次,然后ConfigureServices方法结束后框架会再次调用collection.BuildServiceProvider(),单例又会重新实例化一次。
- 不支持基于async/await和Task的服务解析。C# 不支持异步构造函数;因此建议模式是在同步解析服务后使用异步方法
- 避免在容器中直接存储数据和配置。配置应使用NET CORE的选项模型。
- 避免使用服务定位器模式。例如直接注入IServiceProvider来获取多个需要的服务。PS,如果你的服务依赖项过多,应该考虑分割成几个小功能服务了。
引入第三方IOC框架
.NET CORE 3.x版本后,引入第三方IOC框架的方式变更了,这里不再贴出2.x的方式。以Autofac框架为例。
Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseServiceProviderFactory(new AutofacServiceProviderFactory())
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>()
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
logging.AddConsole();
logging.AddNLog();
});
});
Startup.cs
//原来的 ConfigureServices保留,也可以使用原来的框架继续注入
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMemoryCache();
services.Configure<List<string>>(Configuration.GetSection("BlackPhoneList"));
services.Configure<Dictionary<string, string>>(Configuration.GetSection("BusinessMessages"));
}
//增加ConfigureContainer(ContainerBuilder builder) 方式,使用Autofac框架注入
public void ConfigureContainer(ContainerBuilder builder)
{
builder.RegisterType<PhoneBlackListValidator>().Named<IPhoneValidator>("PHONE_BLACKLIST").SingleInstance();
builder.RegisterType<PhonePerDayCountValidator>().Named<IPhoneValidator>("PHONE_PERDAYCOUNT").SingleInstance();
builder.RegisterType<UniqueIdPerDayCountValidator>().Named<IUniqueIdValidator>("UNIQUEID_PERDAYCOUNT").SingleInstance(); //可遍历类型注入,注意 只支持IEnumerable\IList\ICollection 类型
builder.RegisterType<MessageSendValidator>().As<IMessageSendValidator>().SingleInstance();
}
3.x 主要是在IServiceCollection和IServiceProvider之间增加了一个 ContainerBuilder 容器适配,使得第三方IOC框架引入更加合理了。具体实现原理可以网上源码查找。
特别关注-线程安全
- 创建线程安全的单一实例服务。 如果单例服务依赖于一个Transient服务,那么Transient服务可能也需要线程安全,具体取决于单例使用它的方式。
- 工厂注入方式的Func<IServiceProvider,TService>不需要是线程安全的,框架保证它由单个线程调用一次。
.NET CORE 依赖注入 实践总结的更多相关文章
- ASP.NET Core 依赖注入最佳实践——提示与技巧
在这篇文章,我将分享一些在ASP.NET Core程序中使用依赖注入的个人经验和建议.这些原则背后的动机如下: 高效地设计服务和它们的依赖. 预防多线程问题. 预防内存泄漏. 预防潜在的BUG. 这篇 ...
- ASP.NET Core 依赖注入最佳实践与技巧
ASP.NET Core 依赖注入最佳实践与技巧 原文地址:https://medium.com/volosoft/asp-net-core-dependency-injection-best-pra ...
- ASP.NET Core依赖注入——依赖注入最佳实践
在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...
- ASP.NET Core依赖注入最佳实践,提示&技巧
分享翻译一篇Abp框架作者(Halil İbrahim Kalkan)关于ASP.NET Core依赖注入的博文. 在本文中,我将分享我在ASP.NET Core应用程序中使用依赖注入的经验和建议. ...
- [译]ASP.NET Core依赖注入深入讨论
原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...
- ASP.NET Core 依赖注入(构造函数注入,属性注入等)
原文:ASP.NET Core 依赖注入(构造函数注入,属性注入等) 如果你不熟悉ASP.NET Core依赖注入,先阅读文章: 在ASP.NET Core中使用依赖注入 构造函数注入 构造函数注 ...
- Webservice WCF WebApi 前端数据可视化 前端数据可视化 C# asp.net PhoneGap html5 C# Where 网站分布式开发简介 EntityFramework Core依赖注入上下文方式不同造成内存泄漏了解一下? SQL Server之深入理解STUFF 你必须知道的EntityFramework 6.x和EntityFramework Cor
Webservice WCF WebApi 注明:改编加组合 在.net平台下,有大量的技术让你创建一个HTTP服务,像Web Service,WCF,现在又出了Web API.在.net平台下, ...
- # ASP.NET Core依赖注入解读&使用Autofac替代实现
标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...
- net core 依赖注入问题
net core 依赖注入问题 最近.net core可以跨平台了,这是一个伟大的事情,为了可以赶上两年以后的跨平台部署大潮,我也加入到了学习之列.今天研究的是依赖注入,但是我发现一个问题,困扰我很久 ...
随机推荐
- Fourier Transform
为了在统一框架里分析周期信号与非周期信号,可以给周期信号也建立傅里叶变换. 有两种方法求周期信号的傅里叶变换: **1. 利用傅里叶级数进行构造 ** 对于周期信号\(x(t)\),其傅里叶级数展开式 ...
- Codeforce-CodeCraft-20 (Div. 2)-A. Grade Allocation
n students are taking an exam. The highest possible score at this exam is m. Let ai be the score of ...
- CF思维联系--CodeForces - 218C E - Ice Skating (并查集)
题目地址:24道CF的DIv2 CD题有兴趣可以做一下. ACM思维题训练集合 Bajtek is learning to skate on ice. He's a beginner, so his ...
- 曹工谈并发:Synchronized升级为重量级锁后,靠什么 API 来阻塞自己
背景 因为想知道java中的关键字,对应的操作系统级别的api是啥,本来打算整理几个我知道的出来,但是,尴尬的是,我发现java里最重要的synchronized关键字,我就不知道它对应的api是什么 ...
- csp-j2019游记
我一pj蒟蒻这点水平还来写游记? 算了,毕竟是第一次,记录一下吧 noip->csp 话说我跟竞赛是不是天生八字不合啊...... 小学的时候学小奥,等我开始报名比赛,当时似乎所有竞赛都被叫停了 ...
- 一个epoll的简单例子
epoll事件机制的触发方式有两种:LT(电平触发)和ET(边沿触发) EPOLLIN事件: 内核中的socket接收缓冲区 为空(低电平) 内核中的socket接受缓冲区 不为空(高电平) EPOL ...
- 学习Vue第四节,v-model和双向数据绑定
Vue指令之v-model和双向数据绑定 <!DOCTYPE html> <html> <head> <meta charset="utf-8&qu ...
- 【Java8新特性】还没搞懂函数式接口?赶快过来看看吧!
写在前面 Java8中内置了一些在开发中常用的函数式接口,极大的提高了我们的开发效率.那么,问题来了,你知道都有哪些函数式接口吗? 函数式接口总览 这里,我使用表格的形式来简单说明下Java8中提供的 ...
- leetcode_雇佣 K 名工人的最低成本(优先级队列,堆排序)
题干: 有 N 名工人. 第 i 名工人的工作质量为 quality[i] ,其最低期望工资为 wage[i] . 现在我们想雇佣 K 名工人组成一个工资组.在雇佣 一组 K 名工人时,我们必须按照下 ...
- mercurial 入门
安装 需要python的docutils,故 sudo pip3 install docutils 然后直接安装mercurial sudo pip3 install mercurial 如果超时,则 ...