首页 > 其他分享 >.NET CORE 核心概念:深入理解依赖注入

.NET CORE 核心概念:深入理解依赖注入

时间:2023-06-17 11:33:07浏览次数:32  
标签:count CORE 依赖 扳手 services 实例 ICount NET 注入

1. 为什么要用依赖注入(DI)

什么是依赖注入,为什么要使用呢?简单通俗说就是一个类需要另一个类来协助工作,就产生了依赖,所以需要的依赖项就要【注入】过来一起来协同完成工作。  


软件设计原则中有一个依赖倒置原则(DIP)讲的是要依赖于(1)抽象,不要依赖于具体,(2)高层模块不应该依赖于低层模块, 二者应该依赖于抽象。简单的说就是为了更好的解耦。而控制反转(Ioc)就是这样的一个实现思路, 这个思路的其中一种实现方式就是依赖注入(DI)。


     感觉有点绕, 举个栗子:老李是一个维修工, 现在要出任务去维修, 得先去申领个扳手。


李: "请给我一把可以拧7mm大小的六角螺丝的扳手.", 然后库管老张就从仓库里拿了一把这样的大力牌扳手给老李。


在这个例子中, 维修工老李只要告诉库管我要一个 "可以拧7mm大小的六角螺丝"的扳手即可, 他不用关心扳手的品牌和样式, 也不用采购扳手,更不用关心这个扳手是怎么来的。


   而对于库管, 他只需提供满足这样规则的一个扳手即可, 不用去关心老李拿着这个扳手之后去干什么。所以老李和老张都只是关心"可以拧7mm大小的六角螺丝的"这个规则即可, 也就是说, 如果后期仓库里不再提供大力牌扳手, 而是提供了这样的大牛牌扳手, 无论换了什么牌子和样式, 只要仍满足这个规则, 老李仍然可以正常工作。它们定义了一个规则(比如接口IWrench7mm), 二者都依赖于这个规则, 然后仓库无论提供大力牌(WrenchDaLi : IWrench7mm)还是大牛牌(WrenchDaNiu : IWrench7mm), 都不影响正常工作.


    这就是依赖倒置原则(DIP),  不依赖于具体(牌子),  高层模块(老李)不应该依赖于低层模块(大力牌扳手), 二者应该依赖于抽象(IWrench7mm:可以拧7mm大小的六角螺丝)。如果直接由老李去获取(new)大力牌扳手, 那么当业务改变要求采用大牛牌的时候, 我们就要去修改老李的代码。为了解耦, 在本例中我们只要在配置中让仓库由原来的提供大力牌改为提供大牛牌即可。老李要使用的时候, 可以通过注入(构造器、属性、方法)的方式, 将仓库提供的扳手实例提供给老李使用。


注:仓库继承老李提出的接口(7mm规则)抽象方法,将其对接口方法进行实现


2. 依赖注入理解

引入依赖注入的目的是为了解耦。说白了就是面向接口编程,通过调用接口的方法,而不直接实例化对象去调用。


这样做的好处就是如果添加了另一个实现类,不需要修改之前代码,只需要修改注入的地方将实现类替换。上面说的通过接口调用方法,实际上还是需要去实例化接口的实现类,只不过不需要我们手动new 构造实现类,而是交给如微软的DI、Autofac这些工具去构建实现类。我们只需要告诉它们,某个类是某个接口的实现类,当用到的时候,工具(比如,微软的DI)会自动通过构造函数实例化类。


3. 依赖的服务如何注入

打开Startup这个文件,  看一下里面的ConfigureServices方法。顾名思义,  这个方法是用来配置服务,系统默认已经添加了一些服务, 剩下的就是我们把自己需要的用的添加进去。参数为服务集合IServiceCollection对象,这种对象提供了AddSingleton、AddScoped和AddTransient 三种方法来添加服务,三种方法添加的服务的生命周期不一样。


实例:


添加一个名为DIDemo的.NET CORE MVC项目,在该项目下创建一个服务文件夹(Servers)


1)定义接口ICount


(2)实现接口类Count


至此,服务(类)有了,那么如何能让这个服务为我们所用呢?或者说为我们服务呢?


(3)把类(服务)在Startup文件中通过ConfigureServices方法注入服务。


.NET Core 中自带的DI容器,可以理解为StartUp.CS文件。——不准确


使用容器的好处,由容器来统一管理实例的创建和销毁,你只需要关心怎么用就行了,不需要关系怎么创建跟销毁。


当然容器创建的实例都是有生命周期的。三种创建方法创建的实例生命周期不一样。


Transient: 瞬态模式,每一次访问都会创建一个新的实例

Scoped: 域模式,在同一个Scope内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)。对象在一次请求中是相同的,但在不同请求中是不同的。

Singleton :单例模式,整个应用程序生命周期以内只创建一个实例

此外,常用注入方式有三种。


public void ConfigureServices(IServiceCollection services)


       {


          ……


           //下面先以AddScopend方法阐述下常用的三种注入方式


           //1.最常用的注入方式,以接口形式暴露服务。下面2中方式意思一样


           //1.1 AddScopend后面是(),里面的接口和实现类必须套一层typeof


           services.AddScoped(typeof(ICount), typeof(Count));


           //1.2 AddScopend后面是<>,里面就直接写接口和实现类,当然最后有一个()


           services.AddScoped<ICount, Count>();


           //2.自己注入自己,以实现形式暴露服务


           services.AddScoped(typeof(Count));


           services.AddScoped<Count>();


           //3.需要传参的构造函数的类的注入(后面实例有应用讲解)


           // services.AddScoped(typeof(ICount), sp => { return new Count(参数); }) ;


           //services.AddScoped<ICount>(sp => { return new Count(参数);}) ;


……


       }


4)接下来分析演示三种注入方法的区别:


上面ConfigureServices方法中保留下面的瞬态模式


//第1种:瞬态模式,每一次访问都会创建一个新的实例


services.AddTransient<ICount, Count>();


服务注入之后,我们就要用它。切换到控制器。那么如何能把服务实例注入到控制器中来呢?有属性注入、构造方法注入、方法注入。这里一般会用构造方法注入


public class HomeController : Controller


   {


       private ICount _count;//方便本类其他方法的调用,所以定义一个私有字段来接收


       public HomeController(ICount count)//通过构造方法注入实例,ASP.NET CORE内置了依赖注入容器


       {


           _count = count;


       }


       //说明:请求到home控制器,自然调用home控制器的构造方法,构造方法中需要一个ICount类型的对象,它怎么来的呢?这就是因为.NET Core内置了依赖注入容器,这个时候就会到StartUp.cs文件中的ConfigureServices方法中去找相应的依赖,而在那里告诉了ICount由Count来实现( services.AddTransient<ICount, Count>();),所以这时会去调用Count 的构造方法实例化Count对象。


       //接下来就可以在控制器中使用_count


       public IActionResult Index()


       {


           int c = _count.MyCount();


           ViewBag.count = c;


           return View();


       }


前端展示


运行效果,不断刷新页面也总是0,因为瞬态模式注入的服务,每一次访问都会创建一个新的实例


上面ConfigureServices方法中改为下面的单例模式


//第2种:单例模式,整个应用程序生命周期以内只创建一个实例


services.AddSingleton<ICount, Count>();


运行效果,不断刷新页面不断增加1.


继续把上面ConfigureServices方法中改为下面的域模式


//第3种:域模式,在同一个Scope内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)


services.AddScoped<ICount, Count>();


运行效果,不断刷新页面一直保持为0,因为每次刷新页面都是一个新的请求,所以总是0,在一个请求内产生的实例对象才是唯一。


改进下测试代码


public class HomeController : Controller


   {


       private IServiceProvider _provider;


       private ICount _count;//方便本类其他方法的调用,所以定义一个私有字段来接收


       public HomeController(ICount count,IServiceProvider provider)//通过构造方法注入实例,ASP.NET CORE内置了依赖注入容器


       {


           _count = count;


           _provider = provider;


       }


       //接下来就可以在控制器中使用_count


       public IActionResult Index()


       {


           //int c = _count.MyCount();


           //ViewBag.count = c;


           //注意导入using Microsoft.Extensions.DependencyInjection;


           ICount count1 = _provider.GetService<ICount>();


           ICount count2 = _provider.GetService<ICount>();


           int c1=count1.MyCount();


           int c2 = count2.MyCount();


           ViewBag.c1 = c1;


           ViewBag.c2 = c2;


           //ICount counter1 = _provider.GetService<ICount>();


           //ICount counter2 = _provider.GetService<ICount>();


           //int c1 = counter1.Get();


           //int c2 = counter2.Get();


           return View();


       }


前端调整


测试发现c1、c2分别为0,1


但是每次刷新又重新为0,1


因为每次刷新页面都是一个新的请求,所以总是0,在一个请求内产生的实例对象才是唯一



标签:count,CORE,依赖,扳手,services,实例,ICount,NET,注入
From: https://blog.51cto.com/u_16078995/6504644

相关文章

  • jpa2.2.10 JpaSpecificationExecutor Specification 查询时,实体类中存在@OneToMany,怎
    对于这个问题,您可以在查询时通过join()方法指定不查询@OneToMany关联表。举个例子,如果有一个实体A与实体B是一对多关系,那么您可以这样写查询条件:@OneToMany(targetEntity=DcyRightImage.class,cascade={CascadeType.ALL},fetch=FetchType.EAGER)@JoinColumn(nam......
  • netty rpc个人即兴整理
    nettyrpcrpc不通过http,只是使用了tcphttp应用层tcp传输层http不同产品或者不同语言之间最佳解决浪费传输的宽带rpc为什么只用tcp就可以实现,说白了,http就是编码解码用的,我用prc可以自定义编码解码器,不需要通过http实现rpc个人理解,适用于客户端和服务端都是一个产品组开发......
  • 3.1 卷积神经网路 (Convolutional Neural Networks, CNN)
    1.概念引入:ImageClassification  我们做图像分类时,一般分为三步:所有图片都先rescale成大小一样把每一个类别表示成一个one-hotvector(dimension的长度决定模型可以辨识出多少不同种类的东西)将图片输入到模型中......
  • python自动使用虚拟环境和安装依赖
    代码如下,Windows环境测试通过importosimportplatformimportreimportsysfrompathlibimportPathdefis_venv()->bool:"""判断是否处于虚拟环境(也适用于poetry的)"""ifhasattr(sys,"real_prefix"):returnTruer......
  • .net core使用Html模板转PDF文件并下载的业务类封装
    前言:我这里文件下载的模板选型优先考虑html模板,上手容易,前后端通用,有了模板后就需要有转换了,html转PDF采用第三方包:SelectPdf,下面是代码核心类: 1-PDFService:usingMicrosoft.AspNetCore.Hosting;usingSelectPdf;namespaceMeShop.Domain.PDF{///<summary......
  • C#/VB.NET:快速而简单的免费SVG到PDF转换技巧
    在日常工作中,我们常常需要将SVG转换为PDF格式。这是因为SVG格式的图像在打印时可能会出现问题,例如失去分辨率或无法正确适应纸张大小。与此相比,PDF格式则专门用于打印和共享文档,可以确保高质量输出,并且能够自动适应不同的纸张大小。在本文中,我们将介绍如何使用编程方式将SVG文件转......
  • How Do ASP.NET Core Services Validate JWT Signature Signed by AAD?
    TableofcontentsBackgroundConfigurationHandleAuthenticationValidateTokenSummaryBackgroundIfweneedtouseJWTBearertokensissuedbyAAD(toeitherauserorserviceprincipal)forauthentication,usuallywecanaddbelowcodeto ConfigureSe......
  • Kubernetes 1.27.2集群安装
    基础环境系统Ubuntu22.04.2|主机名称|IP||-----|--------||k8s-master|192.168.198.141||k8s-node01|192.168.198.142||k8s-node02|192.168.198.143|设置k8s环境准备条件(所有机器)#禁用交换分区(在旧版的k8s中kubelet都要求关闭swapoff,但最......
  • .net core 跨域访问
    varbuilder=WebApplication.CreateBuilder(args);builder.Services.AddCors(options=>{   //这定义了一个名为``default``的CORS策略   options.AddPolicy("default",policy=>    {       policy.AllowAnyOrigin()   ......
  • Weak References in .NET
    今天看到一个老外,用C#代码讲WeakReferences的用法不过我发现它的例子应该是用毛病的,在它的例子中weakRef应该没有逃开作用域,不能被正确回收,所以例子的结果也是不准的末尾给出了我对其修改后的例子,给出了两种作用域的对比DecidingWhentoUseWeakReferencesin.NET ......