ASP.NET CORE 内置的IOC解读及使用
在我接触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方法里使用对应生命周期的注入,再调用的地方进行使用,比如构造函数注入等等。
在startup类中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>();
这样就达到了一行代码升级了整个系统的日志系统,业务调用方无需任何的改动。
ASP.NET CORE 内置的IOC解读及使用的更多相关文章
- ASP.NET Core Web 应用程序系列(一)- 使用ASP.NET Core内置的IoC容器DI进行批量依赖注入(MVC当中应用)
在正式进入主题之前我们来看下几个概念: 一.依赖倒置 依赖倒置是编程五大原则之一,即: 1.上层模块不应该依赖于下层模块,它们共同依赖于一个抽象. 2.抽象不能依赖于具体,具体依赖于抽象. 其中上层就 ...
- Asp.Net Core 内置IOC容器的理解
Asp.Net Core 内置IOC容器的理解 01.使用IOC容器的好处 对接口和实现类由原来的零散式管理,到现在的集中式管理. 对类和接口之间的关系,有多种注入模式(构造函数注入.属性注入等). ...
- 第七节:Asp.Net Core内置日志和整合NLog(未完)
一. Asp.Net Core内置日志 1. 默认支持三种输出方式:控制台.调试(底部输出窗口).EventSource,当然也可以在Program类中通过logging.ClearProviders ...
- asp.net core 内置DI容器的一点小理解
DI容器本质上是一个工厂,负责提供向它请求的类型的实例. .net core内置了一个轻量级的DI容器,方便开发人员面向接口编程和依赖倒置(IOC). 具体体现为Micorosoft.Extensio ...
- net core体系-web应用程序-4net core2.0大白话带你入门-8asp.net core 内置DI容器(DependencyInjection,控制翻转)的一点小理解
asp.net core 内置DI容器的一点小理解 DI容器本质上是一个工厂,负责提供向它请求的类型的实例. .net core内置了一个轻量级的DI容器,方便开发人员面向接口编程和依赖倒置(IO ...
- .net core系列之《.net core内置IOC容器ServiceCollection》
一.IOC介绍 IOC:全名(Inversion of Control)-控制反转 IOC意味着我们将对象的创建控制权交给了外部容器,我们不管它是如何创建的,我们只需要知道,当我们想要某个实例时,我们 ...
- [.Net Core] 简单使用 Mvc 内置的 Ioc
简单使用 Mvc 内置的 Ioc 本文基于 .NET Core 2.0. 鉴于网上的文章理论较多,鄙人不才,想整理一份 Hello World(Demo)版的文章. 目录 场景一:简单类的使用 场景二 ...
- [.Net Core] 简单使用 Mvc 内置的 Ioc(续)
简单使用 Mvc 内置的 Ioc(续) 本文基于 .NET Core 2.0. 上一章<[.Net Core] 简单使用 Mvc 内置的 Ioc>已经对日常 Mvc 中的 Ioc 的简单用 ...
- 简单使用 Mvc 内置的 Ioc
简单使用 Mvc 内置的 Ioc 本文基于 .NET Core 2.0. 鉴于网上的文章理论较多,鄙人不才,想整理一份 Hello World(Demo)版的文章. 目录 场景一:简单类的使用 场景二 ...
随机推荐
- Z变换解差分方程的思考
问题描述 今日碰到一道差分方程的题目,如下 [ y(n + 2) - cfrac{7}{10}y(n + 1) + cfrac{1}{10}y(n) = 7x(n+2) -2 x(n + 1) ] 已 ...
- 2019-2020-1 20199324《Linux内核原理与分析》第四周作业
第三章 MenuOs的构造 一.知识点总结 计算机的三大法宝: 存储程序计算机 函数调用堆栈 中断 操作系统的两把宝剑: 中断上下文的切换(保存现场和恢复现场) 进程上下文的切换 它们都和汇编语言有着 ...
- OA项目-xadmin使用
############### xadmin安装和配置 ############### """ 环境: Python3.6.3 django1.11.11 创建dj ...
- git基本操作-长期维护
############### 初识git ############## """ git初识: 1,git是什么?git是一个帮助用户版本控制的软件, 2,g ...
- BZOJ3566 [SHOI2014]概率充电器 (树形DP&概率DP)
3566: [SHOI2014]概率充电器 Description 著名的电子产品品牌 SHOI 刚刚发布了引领世界潮流的下一代电子产品——概率充电器:“采用全新纳米级加工技术,实现元件与导线能否通电 ...
- python学习笔记(22)-os文件操作模块
疑问: 如果打开操作一个文件,是用绝对路径好还是相对路径好? os模块,在lib下面,可以直接引入的,直接使用import. 一.新建一个目录,新建一个文件夹 import os #新建一个文件夹 o ...
- Flask的Jinja2模版过滤器
Jinja2模版过滤器 过滤器是通过管道符号(|)进行使用的,例如:{{ name|length }},将返回name的长度.过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功 ...
- 3.redis kyes命令
Keys命令 1.1设置key的生存时间 Redis在实际使用过程中更多的用作缓存,然而缓存的数据一般都是需要设置生存时间的,即:到期后数据销毁. EXPIRE key seconds ...
- Hadoop_ssh免密认证出现的问题
问题 network is unreachable 网络解决方法 看了大部分文章博客,发现都是说网关或者没ping好,我都试了,发现没用 我的解决方法 把虚拟机右上角的网络连接即可(我的是S开头的一个 ...
- LeetCode No.67,68,69
No.67 AddBinary 二进制求和 题目 给定两个二进制字符串,返回他们的和(用二进制表示). 输入为非空字符串且只包含数字 1 和 0. 示例 输入: a = "11", ...