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 ...
随机推荐
- ES6 进制字面量 All In One
ES6 进制字面量 All In One 二进制 & 八进制 & 字面量 https://developer.mozilla.org/en-US/docs/Web/JavaScript ...
- PHP & LAMP & WAMP
PHP & LAMP & WAMP https://github.com/xgqfrms/DataStructure/issues/7#issuecomment-430538438 h ...
- js 如何打印出 prototype 的查找路径
js 如何打印出 prototype 的查找路径 Function function func (name) { this.name = name || `default name`; } f = n ...
- 市值达万亿?总量仅10万枚的VAST,先兑换先得!
据了解,SPC第一轮.第二轮空投已经结束,两轮空投下来共发放了400万枚SPC.NGK所有算力持有者有效账户基本获得了SPC空投奖励,甚至有的NGK算力持有者获得了数千枚SPC. 而为了进一步奖励NG ...
- NGK Global莫斯科路演:关注内存暴涨和Defi新项目-Baccarat
此次路演由莫斯科演讲师Andrew首先致辞.首先安德鲁回顾了NGK近些年发展的整体情况,表示,NGK技术团队一直在认真的做事并将更加注重技术方案的改进,为行业提供更好的技术解决方案是NGK的愿景,NG ...
- 前端监控SDK开发分享
目录 前言 收集哪些数据 性能 错误 辅助信息 小结 客户端SDK(探针)相关原理和API Web 微信小程序 编写测试用例 单元测试 流程测试 提供Web环境的方式 Mock Web API的方式 ...
- @media屏幕适应
/** 屏幕特殊处理 我们用min-width时,小的放上面大的在下面,同理如果是用max-width那么就是大的在上面,小的在下面 **/ @media screen and (max-width: ...
- 你真的懂 MP4 格式吗?
MP4 文件格式又被称为 MPEG-4 Part 14,出自 MPEG-4 标准第 14 部分 .它是一种多媒体格式容器,广泛用于包装视频和音频数据流.海报.字幕和元数据等.(顺便一提,目前流行的视频 ...
- 用友U8+V12.0安装教程(有需要软件和服务的可以联系我)
有需要用友U8+V12.0软件和服务的可以联系我 QQ:751824677 1.退出所有杀毒软件 2.先装服务器SQL2008 3.服务器(会计): 经典应用模式--全产品 (解压A盘-执行-Aut ...
- TCP/IP协议学习-1.概述
目录 TCP/IP协议概述 分层 延伸知识 FTP例子 为什么需要网络层和传输层 TCP/IP的分层 封装 分用 总结 本文主要摘抄自书籍<TCP/IP详解卷一:协议>与TCP协议相关内容 ...