ASP.NET Core依赖注入初识与思考
一、前言
在上一篇中,我们讲述了什么是控制反转(IoC)以及通过哪些方式实现的。这其中,我们明白了,控制反转(IoC) 是一种软件设计的模式,指导我们设计出更优良,更具有松耦合的程序,而具体的实现方式有依赖注入和依赖查找。
在上篇实例中,我们通过日志的方式举例说明,其中通过代码创建了一个ILogger
的接口,并实现接口实例,基于控制反转的模式,依赖的创建也移交到了外部,但是也发现存在了问题,如果类似存在这样多个接口和实现类,依赖太多,一一创建,没有统一的管理,这反而增加了实际工作量麻烦。
因此我们需要一个可以统一管理系统中所有的依赖的地方,因此,IoC容器诞生了。
容器负责两件事情:
- 绑定服务与实例之间的关系
- 获取实例,并管理实例对象生命周期(创建与销毁)
所以在这一篇中,我们主要讲述Asp.Net Core中内置的IoC容器。
二、说明
在Asp.Net Core中已经为我们集成提供了一个内置的IoC容器,我们可以看到在Startup.cs
的ConfigureServices
中,涉及到依赖注入的核心组件,一个负责实例注册的IServiceCollection
和一个负责提供实例的IServiceProvider
简单的说就是两步:1. 把实例注册到容器中;2. 从容器中获取实例
而在这其中,IServiceCollection
为实现将开发者定义好的实例注册进去提供了三种方法。
分别是:
AddTransient 、AddScoped 、AddSingleton
而这三种不同实例方法也对应的着三种不同的实例生命周期。
三种不同的生命周期:
2.1 暂时性
AddTransient
每次在向服务容器进行请求时都会创建新的实例,这种生存期适合轻量级、 无状态的服务。
2.2 作用域内
AddScoped
在每次Web请求时被创建一次实例,生命周期横贯整次请求。
局部单例对象, 在某个局部内是同一个对象(作用域单例,本质是容器单例);一次请求内是一个单例对象,多次请求则多个不同的单例对象。
2.3 单例
AddSingleton
创建单例生命周期服务的情况如下:
- 在首次请求它们时进行创建;
- 或者在向容器直接提供实现实例时由开发人员进行创建。 很少用到此方法。
其后的每一个后续请求都使用同一个实例。如果开发者的应用需要单例服务情景,推荐的做法是交给服务容器来负责单例的创建和生命周期管理,而不是手动实现单例模式然后由开发者在自定义类中进行操作。
不要从单一实例解析指定了作用域的服务。 当处理后续请求时,它可能会导致服务处于不正确的状态。 可以从范围内或暂时性服务解析单一实例服务。
三、开始
3.1 接口
定义三个接口,分别测试Singleton,Scope,Transient三种,一个 TestService服务
public interface ITransientService
{
string GetGuid();
}
public interface IScopedService
{
string GetGuid();
}
public interface ISingletonService
{
string GetGuid();
}
public interface ITestService
{
public string GetSingletonID();
public string GetTransientID();
public string GetScopedID();
}
3.2 实现
根据上面定义的几种接口,并一一实现对应的接口
public class TransientService : ITransientService
{
public string OperationId { get; }
public TransientService()
{
OperationId = Guid.NewGuid().ToString()[^4..];
}
public string GetGuid()
{
return $"这是一个 Transient service : " + OperationId;
}
}
public class ScopedService : IScopedService
{
public string OperationId { get; }
public ScopedService()
{
OperationId = Guid.NewGuid().ToString()[^4..];
}
public string GetGuid()
{
return $"这是一个 scoped service : "+ OperationId;
}
}
public class SingletonService : ISingletonService
{
public string OperationId { get; }
public SingletonService()
{
OperationId = Guid.NewGuid().ToString()[^4..];
}
public string GetGuid()
{
return $"这是一个 Singleton service : " + OperationId;
}
}
public class TestService : ITestService
{
private ITransientService _transientService;
private IScopedService _scopedService;
private ISingletonService _singletonService;
public TestService(ITransientService transientService, IScopedService scopedService, ISingletonService singletonService)
{
_transientService = transientService;
_scopedService = scopedService;
_singletonService = singletonService;
}
public string GetSingletonID()
{
return _singletonService.GetGuid();
}
public string GetTransientID()
{
return _transientService.GetGuid();
}
public string GetScopedID()
{
return _scopedService.GetGuid();
}
}
3.3 注入
在Startup.cs
类文件ConfigureServices
方法中,注入依赖
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddTransient<ITransientService, TransientService>();
services.AddSingleton<ISingletonService, SingletonService>();
services.AddScoped<IScopedService, ScopedService>();
services.AddScoped<ITestService, TestService>();
}
3.4 调用
定义一个控制器,实现调用
[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
private ITransientService _transientService;
private IScopedService _scopedService;
private ISingletonService _singletonService;
private ITestService _testService;
public TestController(ITransientService transientService, IScopedService scopedService, ISingletonService singletonService,
ITestService testService
)
{
_transientService = transientService;
_scopedService = scopedService;
_singletonService = singletonService;
_testService = testService;
}
[HttpGet]
public JsonResult Get()
{
var data1 = _transientService.GetGuid();
var data2 = _testService.GetTransientID();
var data3 = _scopedService.GetGuid();
var data4 = _testService.GetScopedID();
var data5 = _singletonService.GetGuid();
var data6 = _testService.GetSingletonID();
return new JsonResult(new {
data1,
data2,
data3 ,
data4,
data5,
data6,
});
}
}
在上面中我们了解到,注入的方式一般有三种,构造函数注入, 方法注入,属性注入,而在ASP.NET Core中自带的这个IoC容器,默认采用了构造函数注入的方式。
3.5 测试
启动运行项目,访问接口/Test
效果如下:
3.6 对比
对比两次的请求访问可以发现,上面我们一共得到了 4个Transient实例,2个Scope实例,1个Singleton实例。
在请求中,AddSingleton方式的id值相同;
AddScope方式两次请求之间不同,但同一请求内是相同的;
AddTransient方式在同一请求内的多次注入间都不相同。
3.7小结
通过上述的代码示例,也证实了之前的三种方式对应不同生命周期的说明。
暂时性(Transient) : 生命周期是每次获得对象都是一次新的对象,每一次都不一样。
作用域内(Scoped) : 生命周期是在每一次请求作用域内是同一个对象,非作用域内则是新的对象。
单例(Singletion) : 生命周期是这个服务启动后都是一个对象,也即是全局单例对象
。
四、其他Ioc容器
通过上述的了解,我们知道IoC容器是一个依赖注入框架,在.NET Core中也提供了内置的IoC容器,通过AddXXX方法来实例依赖对象,而在实际开发中,就是每一个实例都需要一个个的添加,这样的实现方式在小项目中还好,但是如果在复杂大型的项目中,就略向麻烦些,可能需要添加很多方法来实现,整体的可观性也不好。
为了达到可以简化我们工作量,应该采用批量注册,因此我们也可以引入其他的Ioc容器框架,实现更多的功能和扩展。
在平时开发中,常用的IoC框架有很多,而在这里我们选择用Autofac,这也是在.net下比较流行的,其他的框架不做说明,可自行查阅了解。
ASP.Net Core中使用Autofac 框架注入 (在后续篇章会具体说明)
五、思考
在实际的开发中,对于这几种生命周期,我们应该如何应用呢?
比如,在像DBContext这种实例,在实际开发中是用Transient 还是Scoped呢?
建议使用Scoped
因为有些对象在请求中可以需要用到多个方法或者多个Service、Repository的时候,为了减少实例初始化的消耗,实现事务功能,可以在整个请求的生命周期共用一个Scope。
其他的思考:
- ASP.NET Core中,默认采用了构造函数注入的方式,如果采用属性注入或者方法注入,又该怎么实现?
- 一个接口多种实现的时候,我们将多种实现都给注入进了依赖注入容器中,但是在服务调用的时候总是获取到最后注入的那个方法的实现,这时候就在想能不能实现动态的选择使用哪种实现呢?
- 一个作用域(Scoped)服务中注入一个瞬时(Transient)服务时,瞬时服务中的值还会每次都变化吗?
- 链式注入时,生存期的选择,三种注入方式的权重问题
以上的思考,大家可以说说自己的想法
在后续的篇章中,也会对这些问题,进行深入讨论说明。
六、总结
本篇主要介绍了什么是IoC容器,了解到它是DI构造函注入的框架,它管理着依赖项的生命周期以及映射关系,同时也介绍实践了在ASP.Net Core中,默认提供的内置IoC容器,以及它的实例注册方式和相应的生命周期。
好啦,这篇文章就先讲述到这里吧,在后续篇章中会对ASP.Net Core中使用Autofac 框架实践说明,希望对大家有所帮助。
如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。
ASP.NET Core依赖注入初识与思考的更多相关文章
- # ASP.NET Core依赖注入解读&使用Autofac替代实现
标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...
- 实现BUG自动检测 - ASP.NET Core依赖注入
我个人比较懒,能自动做的事绝不手动做,最近在用ASP.NET Core写一个项目,过程中会积累一些方便的工具类或框架,分享出来欢迎大家点评. 如果以后有时间的话,我打算写一个系列的[实现BUG自动检测 ...
- [译]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 依赖注入, 学习一下基本使用 然后学习一招, 不使用接口规范, 直接写功能类, 一般情况下可以用来做单例. 参考https://www.cnblo ...
- ASP.NET Core依赖注入——依赖注入最佳实践
在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...
- 自动化CodeReview - ASP.NET Core依赖注入
自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 我个人比较懒,能自动做的事绝 ...
- ASP.NET Core 依赖注入最佳实践——提示与技巧
在这篇文章,我将分享一些在ASP.NET Core程序中使用依赖注入的个人经验和建议.这些原则背后的动机如下: 高效地设计服务和它们的依赖. 预防多线程问题. 预防内存泄漏. 预防潜在的BUG. 这篇 ...
- ASP.NET Core依赖注入最佳实践,提示&技巧
分享翻译一篇Abp框架作者(Halil İbrahim Kalkan)关于ASP.NET Core依赖注入的博文. 在本文中,我将分享我在ASP.NET Core应用程序中使用依赖注入的经验和建议. ...
- ASP.NET Core依赖注入解读&使用Autofac替代实现【转载】
ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Autofac实现和自定义实现扩展方法 3.1 安装Autof ...
随机推荐
- TensorFlow & Machine Learning
TensorFlow & Machine Learning TensorFlow 实战 传统方式 规则 + 数据集 => 答案 无监督学习 机器学习 神经元网络 答案 + 数据集 =&g ...
- git alias all in one
git alias all in one workspace:工作区 staging area:暂存区/缓存区 local repository:或本地仓库 remote repository:远程仓 ...
- HTML spaces types: &   &  
HTML spaces types: & & What is the difference between and https://stackoverflow.co ...
- WebAssembly in Action
WebAssembly in Action 数据加密,反爬虫,防盗链,版权保护,数据追踪,埋点 blogs 加密,js 禁用检测,权限控制 WebAssembly 防盗链 wasm online id ...
- Linux & SIGUSER1
Linux & SIGUSER1 https://stackoverflow.com/questions/10824886/how-to-signal-an-application-witho ...
- POST 非幂等
POST 非幂等 HTTP幂等方法,是指无论调用这个url多少次,都不会有不同的结果的HTTP方法; 也就是不管你调用1次还是调用100次,1000次,结果都是一样的(前提是服务器端的数据没有被人为手 ...
- 大送福利!市场香饽饽VAST到底什么来头,为何被高价估值
近日,NGK星空计划新币VAST成为了香饽饽,还未正式上线前,市场讨论的热度就居高不下.如今NGK推出1万VAST免费送新人福利更是将这波热度推向了高潮. 具体福利规则:在美国加州时间2021年2月8 ...
- 聊聊ASP.NET Core中的配置
作为软件开发人员,我们当然喜欢一些可配置选项,尤其是当它允许我们改变应用程序的行为而无需修改或编译我们的应用程序时.无论你是使用新的还是旧的.NET时,可能希望利用json文件的配置.在这篇文章中, ...
- spring boot用ModelAndView向Thymeleaf模板传参数
最近在调试一个Spring Boot向Thymeleaf模板传参数的例子,但踩了很多坑,这里就把详细过程记录下来,以供大家参考. 先说下,这里遇到哪些坑呢? 1 我用的是IDEA社区版,这不支持JSP ...
- 搭建SSH框架
以下为链接地址:https://www.2cto.com/kf/201606/518341.html