ASP.NET Core依赖注入初识与思考

一、前言

在上一篇中,我们讲述了什么是控制反转(IoC)以及通过哪些方式实现的。这其中,我们明白了,「控制反转(IoC)」 是一种软件设计的模式,指导我们设计出更优良,更具有松耦合的程序,而具体的实现方式有「依赖注入」「依赖查找」

在上篇实例中,我们通过日志的方式举例说明,其中通过代码创建了一个ILogger的接口,并实现接口实例,基于控制反转的模式,依赖的创建也移交到了外部,但是也发现存在了问题,如果类似存在这样多个接口和实现类,依赖太多,一一创建,没有统一的管理,这反而增加了实际工作量麻烦。

因此我们需要一个可以统一管理系统中所有的依赖的地方,因此,IoC容器诞生了。

容器负责两件事情:

  • 绑定服务与实例之间的关系

  • 获取实例,并管理实例对象生命周期(创建与销毁)

所以在这一篇中,我们主要讲述Asp.Net Core中内置的IoC容器。

二、说明

在Asp.Net Core中已经为我们集成提供了一个内置的IoC容器,我们可以看到在Startup.csConfigureServices中,涉及到依赖注入的核心组件,一个「负责实例注册」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。

其他的思考:

  1. ASP.NET Core中,默认采用了构造函数注入的方式,如果采用属性注入或者方法注入,又该怎么实现?

  2. 一个接口多种实现的时候,我们将多种实现都给注入进了依赖注入容器中,但是在服务调用的时候总是获取到最后注入的那个方法的实现,这时候就在想能不能实现动态的选择使用哪种实现呢?

  3. 一个作用域(Scoped)服务中注入一个瞬时(Transient)服务时,瞬时服务中的值还会每次都变化吗?

  4. 链式注入时,生存期的选择,三种注入方式的权重问题

以上的思考,大家可以说说自己的想法。

「在后续的篇章中,也会对这些问题,进行深入讨论说明。」

六、总结

本篇主要介绍了什么是IoC容器,了解到它是DI构造函注入的框架,它管理着依赖项的生命周期以及映射关系,同时也介绍实践了在ASP.Net Core中,默认提供的内置IoC容器,以及它的实例注册方式和相应的生命周期。

好啦,这篇文章就先讲述到这里吧,「在后续篇章中会对ASP.Net Core中使用Autofac 框架实践说明」,希望对大家有所帮助。

如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。????

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/303525.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

memcached mysql缓存_memcached做数据库缓存

最近研究memcache小有成果&#xff0c;把经验分享出来。白话:很早就听说memcache了&#xff0c;一直没搞懂&#xff0c;后来又看到redis很火&#xff0c;可以用来做缓存&#xff0c;研究了半天也没搞懂咋个做缓存&#xff0c;后来也不纠结了&#xff0c;继续学习python,当对pyt…

掌握Python爬虫基础,仅需1小时!

随着互联网的发展&#xff0c;google、百度等搜索引擎让我们获取信息愈加方便。但需求总会不断涌现&#xff0c;纯粹地借助百度等收集信息是远远不够的&#xff0c;因此编写爬虫爬取信息的重要性就越发凸显。比如有人为了炒股&#xff0c;专门爬取了多种股票信息&#xff1b;也…

我看ITIL在中国(六):如何建立有中国特色的IT运维管理平台【二】

在开始筹划建设有“中国特色的IT运维管理平台”之前&#xff0c;先来看看我们目前面临的情况&#xff1a; 随着信息化建设的不断深入&#xff0c;各种企业的核心业务都逐步地迁移到IT平台上来&#xff0c;对IT管理的要求也越来越高&#xff0c;IT需要管理&#xff0c;向IT管理要…

史上首次!世界杯使用视频裁判

2018年6月16日18时&#xff0c;法国队在喀山中央球场迎来了他们本届世界杯的首场比赛&#xff0c;对手是澳大利亚队。比赛进行到第56分钟&#xff0c;格列兹曼接到队友的直塞球&#xff0c;单刀杀入禁区&#xff0c;澳大利亚后卫里斯登铲球&#xff0c;但并没有碰到皮球&#x…

二分查找和折半插入排序一块说说-很合适~~~

前言上一篇在聊时间复杂度和空间复杂度时&#xff0c;没有按指定格式显示(明明预览的时候没问题的)&#xff0c;强迫症的我稍微优化了一下重新发布&#xff0c;目的就是让小伙伴看着舒服。上次聊到的直接插入排序在比较有序数据和待插入数据时&#xff0c;是通过依次遍历的方式…

用Python更加了解微信好友

用了微信几年了&#xff0c;微信号有也不少了&#xff0c;但是真正了解自己的好友吗&#xff1f;好友最多的城市是哪个&#xff1f;好友男女比例是多少&#xff1f;好友签名都是什么&#xff1f;今天我们来充分了解自己的微信好友。运行平台&#xff1a; Windows Python版本&a…

Linux下配置DNS

Linux下配置DNS一、配置环境1.Linux操作系统版本&#xff1a;RedHat AS 52.网络环境设置&#xff1a;IP&#xff1a;192.168.1.1 NetMark&#xff1a;255.255.255.0 Getway&#xff1a;192.168.1.13.软件包的准备&#xff1a;1&#xff09;bind-9.3.3-7.el5.i386.rpm …

python用pandas提取行列_python- pandas 不删除符合条件的行和列

我正在尝试建立一个回归模型,以便根据出现的单词来预测收视率(1-5)(回归本身并不一定表现良好,更多的是关于所采用的方法).我使用以下代码创建了一个词频矩阵&#xff1a;bow df.Review2.str.split().apply(pd.Series.value_counts)看起来像这样&#xff1a;我现在有兴趣删除在…

中国宜坚持发展自主操作系统

一直以来&#xff0c;我国IT产业存在“缺芯少魂”的问题&#xff0c;芯指的是芯片&#xff0c;魂指的是操作系统。操作系统是连接硬件和应用软件的媒介和桥梁。如果无法在操作系统方面实现安全可控&#xff0c;整个信息安全就无从谈起&#xff0c;而恰恰当下国内桌面操作系统市…

老板啥都懂,还天天套路我?!

今天我被老板叫到办公室进行了一场“推心置腹”的对话▼来源&#xff1a;特大号文章版权归原作者所有&#xff0c;转载仅供学习使用&#xff0c;不用于任何商业用途&#xff0c;如有侵权请留言联系删除&#xff0c;感谢合作。

如何提高电脑办公效能

其实你只要花一点点的时间&#xff0c;就可以使你的生活变得更好。在电脑办公上&#xff0c;记忆几个快捷键、安装一些实用的软件等绝对会让你的效率翻倍&#xff01; 在此弥缝分享几条我常常使用到的技巧和软件&#xff1a; 熟练一些快捷键 熟练的快捷键越多&#xff0c;做事越…

BeetleX.Http.Clients访问https服务

最近在做数据分析平台&#xff0c;那在做这个产品的时最需要的自然是测试数据&#xff0c;自己去构建行业测试数据比较麻烦&#xff0c;看到有同行产品的演示数据当然不能错过。由于采集过程中使用到BeetleX.Http.Clients去抓取第三方的Https接口数据&#xff0c;所以顺便记录一…

这一次,用数据解读玩家行为,用实力拿下预测大奖!

如今&#xff0c;大数据落地应用已十分广泛&#xff0c;除了政务、金融、医疗、旅游等传统行业外&#xff0c;在游戏行业中的应用也逐渐受人瞩目。那么&#xff0c;当大数据遇上游戏产业&#xff0c;会产生哪些变革和创新呢&#xff1f;众所周知&#xff0c;全球游戏市场规模庞…

如何在 C# 中使用隐式和显式操作符

C# 有一个鲜为人知的特性是通过定义 显式和隐式操作符 实现类型之间的转换&#xff0c;这篇文章我们将会讨论如何使用这些 显式 和 隐式 操作符。什么是显式&#xff0c;什么是隐式 隐式类型转换 它是运行时自动帮你完成的&#xff0c;言外之意就是你不需要人为干预&#xff0c…

SUSE10下配置FTP服务

SUSE10 liuux下配置FTP和SUSE9有不同之处&#xff0c;大家请注意。下面为SUSE10下ftp服务配置具体步骤。 Linux 系统安装完成后&#xff0c;默认不会开启FTP 服务&#xff0c;需要在yast界面下进行手动启动: 说明&#xff1a;如果未安装FTP 包&#xff0c;则需要先行安装FTP 软…

java gzip 解压文件_Java实现文件压缩与解压[zip格式,gzip格式]

原文&#xff1a;http://www.cnblogs.com/visec479/p/4112881.html#3069573Java实现ZIP的解压与压缩功能基本都是使用了Java的多肽和递归技术&#xff0c;可以对单个文件和任意级联文件夹进行压缩和解压&#xff0c;对于一些初学者来说是个很不错的实例。zip扮演着归档和压缩两…

来看一场 AI 重建的 3D 全息世界杯比赛!

世界杯来了&#xff01;央视名嘴白岩松调侃 “俄罗斯世界杯&#xff0c;中国除了足球队没去&#xff0c;其他的都去了”&#xff0c;这届世界杯&#xff0c;中国球迷购买球票的数量在所有国家中排名第 9&#xff0c;可见球迷对世界杯的热情。那么&#xff0c;除了准备好小龙虾在…

编译Linux 2.6内核

编译内核易如反掌。让人叹为观止的是&#xff0c;这实际上比编译和安装像glibc这样的系统级组伴还要简单。2.6内核提供了一套新工具&#xff0c;使编译内核更加容易&#xff0c;比早期发布的内核有了长足的进步。 2.3.1 配置内核 因为Linux源码随手可得&#xff0c;那就意味着在…

获取父线程 java_java子线程中获取父线程的threadLocal中的值

我们都知道线程本地变量表也就是ThreadLocal在我们做线程级的数据隔离时非常好用&#xff0c;但是有时候我们会想如何让子线程获取到父线程的ThreadLocal&#xff0c;其实在线程中除了ThreadLocal外还有InheritableThreadLocal&#xff0c;顾名思义&#xff0c;可继承的线程变量…

源码分享,送你一份Google Python class源码

几年前&#xff0c;Google推出Python课堂。Google Python课堂:https://developers.google.com/edu/python/小编也整理了一下Google Python课堂的源码&#xff0c;里面有四个例子。logpuzzle这个例子主要是利用urllib模块做一个图片拼接的小游戏,主要是从一大堆的网页里面解析分…