在我接触IOC和DI 概念的时候是在2016年有幸倒腾Java的时候第一次接触,当时对这两个概念很是模糊;后来由于各种原因又回到.net 大本营,又再次接触了IOC和DI,也算终于搞清楚了IOC和DI 这两个概念关系。使用过ASP.NET Core的人对这两个概念一定不陌生,想必很多人还是很难去理解这两个东西,所以,趁着今天有空,就去把两个概念捋清楚,并将学习过程的知识点记录下来。

一、概念

1.1 什么是IOC?

Ioc—Inversion of Control,即控制反转,其是一种设计思想,而不是一种技术。再没有使用IOC之前,我们一般是通过new来实例化,从而创建一个对象。但是我们使用IOC之后,创建这个对象的控制权将由内部转换到外部,那么这个过程便可以理解为控制反转。也即把对象转换成抽象对象的依赖.。

同时控制反转也是一个目标,控制反转的优点有如下两点:

  • 可以很好的做到解耦
  • 屏蔽对象的实现细节,只关心动作不关心动作中的细节。

1.2 什么是DI(依赖注入)?

全称为Dependency Injection,意思自身对象中的内置对象是通过注入的方式进行创建。形象的说,即由容器动态的将某个依赖关系注入到组件之中。

1.3 IOC和DI的联系?

IOC是一种设计思想,而DI是这种设计思想的一个实现。理解IOC和DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”。
●谁依赖于谁:当然是应用程序依赖于IoC容器;
●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源;
●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象;
●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)

1.4 常见的IOC框架。

微软.net core 内置的DI、Autofac、Unity


以上已经把IOC和DI 这两个联系简要捋清楚了,下面我们一起学习.net core 内置的DI使用。

二、内置IOC

2.1 内置的IOC 有三种生命周期

  • Transient:瞬时生命周期, Transient服务在每次被请求时都会被创建一个新的对象。这种生命周期比较适用于轻量级的无状态服务。
  • Scoped: Scoped生命周期的服务是每次web请求被创建,局部单例对象, 在某个局部内是同一个对象(作用域单例,本质是容器单例);一次请求内是一个单例对象,多次请求则多个不同的单例对象.
  • Singleton: Singleton生命能够周期服务在第一被请求时创建,在后续的每个请求都会使用同一个实例。如果你的应用需要单例服务,推荐的做法是交给服务容器来负责单例的创建和生命周期管理,而不是自己来走这些事情。

我们先来看一张图:

ASP.NET Core本身已经集成了一个轻量级的IOC容器,开发者只需要定义好接口后(抽象),并且对抽象的接口进行实现,再Startup.cs的ConfigureServices方法里使用对应生命周期的注入,再调用的地方进行使用,比如构造函数注入等等。

在start.up类中ConfigureServices方法对实例进行注册如下代码:

 // This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){Console.WriteLine("ConfigureServices");services.AddControllersWithViews();//注入生命周期为单例的服务services.AddSingleton<ISingletonService, SingletonService>();//注入生命周期为Scoped 的服务services.AddScoped<IScopedService, ScopedService>();//注入生命周期为瞬时的服务services.AddTransient<ITransientService, TransientService>();}

上面代码我分别注册了单例瞬时作用域的生命周期的服务。

下面简单写了一个例子让大家看看这三个生命周期的实例的代码

三个生命周期的抽象服务实现代码如下:

public class ScopedService : IScopedService
{public string GetInfo(){return $"this is scoped service ";}
}public class SingletonService : ISingletonService
{public string GetInfo(){return $"this is singleton service";}
}public class TransientService : ITransientService
{public string GetInfo(){return $"this is transient service";}
}

控制器代码如下:

public IActionResult Index(){using (IServiceScope scope = HttpContext.RequestServices.CreateScope()){var transientService = scope.ServiceProvider.GetService<ITransientService>();var transientService2 = scope.ServiceProvider.GetService<ITransientService>();var result = $"{transientService.GetInfo()} hashCode : {transientService.GetHashCode()} <br/>";result += $"{transientService2.GetInfo()} hashCode : {transientService2.GetHashCode()} <br/>";ViewBag.Transient = result;var scopeService= scope.ServiceProvider.GetService<IScopedService>();var scopeService2 = scope.ServiceProvider.GetService<IScopedService>();result = $"{scopeService.GetInfo()} hashCode :{ scopeService.GetHashCode()} <br/>";result += $"{scopeService2.GetInfo()} hashCode :{ scopeService2.GetHashCode()} <br/>";ViewBag.Scope = result;var singletonService = scope.ServiceProvider.GetService<ISingletonService>();var singletonService2 = scope.ServiceProvider.GetService<ISingletonService>();result = $"{singletonService.GetInfo()} hashCode:{ singletonService.GetHashCode()} <br/>";result += $"{singletonService2.GetInfo()} hashCode:{ singletonService2.GetHashCode()} <br/>";ViewBag.Singletion = result;}return View();}

index.cshtml 视图代码如下:

@{ViewData["Title"] = "Home Page";
}<b>Transient生命周期</b>
<div>@Html.Raw(ViewBag.Transient)
</div><b>Scoped生命周期</b>
<div>@Html.Raw(ViewBag.Scope)
</div><b>Singletion生命周期</b>
<div>@Html.Raw(ViewBag.Singletion)
</div>

分别运行两次的结果如下图:


从上图的运行的每个对象的hashCode 的结果看出Transient生命周期是每次获得对象都是一次新的对象;Scoped生命周期是在作用域是同一个对象,非作用域内则是新的对象;Singletion生命周期是最好理解的,是这个服务启动后都是一个对象,也即是全局单例对象

2.2 注入的几种方式

直接注入IServiceProvider的方式

services.AddSingleton();

然后在构造函数中通过如下方式获取具体实现

public HomeController(IServiceProvider serviceProvider)
{var singletonService = serviceProvider.GetService<SingletonService>();
}

通过GetServices方式

services.AddSingleton<ISingletonService, SingletonService>();

然后在构造函数中通过如下方式获取具体实现

public HomeController(IServiceProvider serviceProvider){var singletonService = serviceProvider.GetService<ISingletonService>();}

构造函数直接注入方式(推荐)

public HomeController(ISingletonService singletonService){var _singletonService =singletonService;}

集合方式注入

这种方式其实就是省去了注入IServiceProvider的过程,直接将GetServices获取的结果进行注入。首先注入interface及具体实现

services.AddSingleton<ISingletonService, SingletonService1>();
services.AddSingleton<ISingletonService, SingletonService2>();

获取的方式如下

public HomeController(IEnumerable<ISingletonService> services)
{var singletoService1 = services.First();var singletoService2 = services.Skip(1).First();
}

工厂方式注入

然后我们继续注入Func这个工厂,这里我们按int来返回不同的实现,当然你也可以采用其他方式比如string

services.AddSingleton(provider =>
{Func<int, ISingletonService> func = n =>{switch (n){case 1:return provider.GetService<SingletonService1>();case 2:return provider.GetService<SingletonService2>();default:throw new NotSupportedException();}};return func;
});

然后在构造函数中通过如下方式获取具体实现

public HomeController(Func<int, ISingletonService> funcFactory)
{var singletonService1 = funcFactory(1);var singletonService2 = funcFactory(2);
}

除了以上的几个注入方式外,还可以通过反射的方式批量注入程序集的方式,这里就不一一写出具体的例子,自己去尝试。

三、IOC怎么解耦?

学习到这里,大家对IOC和DI 的使用已经有了一定的掌握,上面我提到过IOC的目标是解耦屏蔽对象的实现细节这两大优点;再来回顾上面的代码实现 可以发现,推荐的注入方式是通过抽象接口的方式进行注入而不是直接注入对象方式。

现在我列举一个企业发展过程中很常见的一个例子,比如:我在一家企业担任开发工作,开发了一个电商平台系统,系统中需要用到日志系统,由于当时的各种外在环境,我们使用的日志是nlog这个日志组件;但是经过平台的不断发展后,nlog 日志组件已经不能满足我们平台的需求,需要寻求更智能的日志系统,比如Exceptionless,这时候我们就不得不权衡下现有代码的可维护性。刚好这个电商平台系统代码使用了IOC 使得代码可维护性比较强,日志系统耦合性比较低,只需要简单的几行代码即可实现日志系统的大换血。现在来看下电商系统目前使用的日志系统相关的代码。

日志组件服务注册如下代码:

services.AddSingleton<ILogService, nLogService>();

各业务中使用nlog代码大概如下:

public HomeController(ILogService LogService){_logService =LogService;_logService.Info("=========开始访问========");}

从上面的代码中使用日志的相关业务代码都是通过IOC来进行控制反转调用日志服务,隐藏了日志服务业务的实现细节;使用业务方无需关注日志的实现细节,从而达到 了高度解耦的效果-屏蔽对象实现细节

现在我们进行日志系统大换血代码只需要实现一个新的日志服务,我这里创建ExceptionlessLogService类继承ILogService即可,同时安排对应的人去实现ExceptionlessLogService这个类就可以达到日志系统升级的效果。

更换后的代码如下:

services.AddSingleton<ILogService, NLogService>();
改成
services.AddSingleton<ILogService, ExceptionlessLogService>();

这样就达到了一行代码升级了整个系统的日志系统,业务调用方无需任何的改动。

欢迎各位大佬关注dotNET 博士公众号,在这里有各行各业的大佬一起学习探讨

ASP.NET CORE 内置的IOC解读及使用相关推荐

  1. ASP.NET Core Web 应用程序系列(一)- 使用ASP.NET Core内置的IoC容器DI进行批量依赖注入(MVC当中应用)...

    在正式进入主题之前我们来看下几个概念: 一.依赖倒置 依赖倒置是编程五大原则之一,即: 1.上层模块不应该依赖于下层模块,它们共同依赖于一个抽象. 2.抽象不能依赖于具体,具体依赖于抽象. 其中上层就 ...

  2. 内置哪几种服务容器_ASP.NET CORE 内置的IOC解读及使用

    在我接触IOC和DI 概念的时候是在2016年有幸倒腾Java的时候第一次接触,当时对这两个概念很是模糊:后来由于各种原因又回到.net 大本营,又再次接触了IOC和DI,也算终于搞清楚了IOC和DI ...

  3. 第七节:Asp.Net Core内置日志记录

    111 转载于:https://www.cnblogs.com/yaopengfei/p/10850068.html

  4. ASP.NET的内置对象介绍

    ASP.NET的内置对象介绍 1.Response 2.Request 3.Server 4.Application 5.Session 6.Cooki Request对象主要是让服务器取得客户端浏览 ...

  5. 用Autofac替换.net core 内置容器

    官方建议使用内置容器,但有些功能并不支持,如下: 属性注入 基于名称的注入 子容器 自定义生存期管理 Func<T> 支持 所以可以使用其他第三方IOC容器,如Autofac,下面为学习使 ...

  6. ASP.NET Core中使用IOC三部曲(一.使用ASP.NET Core自带的IOC容器)

    前言 本文主要是详解一下在ASP.NET Core中,自带的IOC容器相关的使用方式和注入类型的生命周期.这里就不详细的赘述IOC是什么 以及DI是什么了.. emm..不懂的可以自行百度. 正文 今 ...

  7. ASP.NET的内置对象

    Request 该对象用于检索从浏览器向服务器所发送的请求中的信息.在按下"提交"按钮时,Request对象将读取和提取通过HTTP请求发送的参数.在用户提交表单时,包含在输入控件 ...

  8. ASP.NET Core技术研究-探秘依赖注入框架

    ASP.NET Core在底层内置了一个依赖注入框架,通过依赖注入的方式注册服务.提供服务.依赖注入不仅服务于ASP.NET Core自身,同时也是应用程序的服务提供者. 毫不夸张的说,ASP.NET ...

  9. ASP.NET Core 介绍

    原文:Introduction to ASP.NET Core 作者:Daniel Roth.Rick Anderson.Shaun Luttin 翻译:江振宇(Kerry Jiang) 校对:许登洋 ...

最新文章

  1. 转载:Ajax及 GET、POST 区别
  2. android 怎么判断activity 从哪里启动的
  3. CentOS 7 下的 Firewall
  4. mysql视图表怎么设置约束_MySQL一一sql的视图、索引、约束
  5. MySQL保存或更新 saveOrUpdate
  6. ubuntu16.04 kinetic外接Intel Realsense D435i配置教程
  7. 数据结构 5-2 二叉树建树
  8. 度量分析之报告信息的四个层次:数据,信息,分析,措施
  9. [每日一题] OCP1z0-047 :2013-07-14 正则表达式
  10. 实用的 Python —— 快速进行相关计算
  11. 商城购物车php代码,php实现商城购物车的思路和源码分析
  12. QueryDSL依赖
  13. 权重计算方法一:层次分析法(AHP)
  14. mysql数据库的基本管理
  15. Echarts + Web实现大屏展示效果
  16. Word怎样设置处理表格与表格标题之间的间距问题
  17. 【神DP】-ZOJ-3623-Battle Ships
  18. 北明有“渔”,其名为“鲲”
  19. 计算机中丢失等d3dx9,Win10 提示quot;计算机丢失d3dx9_43.dllquot;怎么办_Win10镜像之家...
  20. 计算明天的日期C语言,c语言源代码(计算明天的日期)

热门文章

  1. 文献阅读-FCER1G与透明细胞癌中巨噬细胞的浸润相关并且通过调节肿瘤免疫产生不良预后
  2. 利用CSS调整图片大小
  3. 终于找到了ubuntu卡的原因!
  4. 系统跟服务器的区别,域名服务器与根服务器区别
  5. 关于NS-2仿真中移动节点的设置
  6. strcpy、strncpy、strncpy_s和snprintf
  7. 马克下, 初识 runtime (一)
  8. vue 移动端头像裁剪_移动端 上传头像 并裁剪功能(h5)
  9. python计算向量的模_计算Python Numpy向量之间的欧氏距离实例
  10. Android 百度地图 SDK v3.0.0 (四) 引入离线地图功能