.NET Core ASP.NET Core Basic 1-2 控制反转与依赖注入
.NET Core ASP.NET Core Basic 1-2
本节内容为控制反转与依赖注入
简介
控制反转IOC
这个内容事实上在我们的C#高级篇就已经有所讲解,控制反转是一种设计模式,你可以这样理解控制反转,假设有一个人他有一部A品牌手机,他用手机进行听歌、打游戏,那么你可以创建一个手机类和一个人类
class APhone : IPhone
{
public string Owner{get;set;}
public Phone(string own)
{
Owner = own;
}
void Play()
{
//省略
}
void Music()
{
//省略
}
}
class Man
{
public string Name{get;set;}
void Game()
{
var p = new APhone(Name);
p.Play();
}
}
事实上这段代码的耦合度是比较高的?它使用的是正转,也就是我需要什么东西的时候我就自己创建一个这个东西。为什么说他不好呢,如果有一天这个人决定再也不使用A品牌手机了,他决定以后只使用B品牌。那么也就意味着整个的Man类使用过APhone类的地方都需要更改。这是一个非常麻烦的事情,我们这个时候就需要运用我们的IOC控制反转了。我们将实例或者是需要使用的对象的创建交给你的调用者,自己只负责使用,其它人丢给你依赖的这个过程理解为注入。
控制反转的核心就是——原本我保存使用我自己的东西,现在我把控制权交给我的上级,我需要使用的时候再向他要。这个时候,接口的作用不言而喻,A继承了Phone接口,B也继承了,假定我们一开始就使用Phone接口去创建不同的A,B对象,那么是不是可以有效的切换AB对象呢?
依赖注入
依赖注入体现的是一个IOC(控制反转),它非常的简单,我们之前的Man类代码中使用的是正转的方式,也就是我要一个对象,那么我就创建一个。现在我们使用依赖注入就是将我们对这个对象的控制权交给上一级接口,也就成为了这种,我想要一个对象,我就向上级发出请求,上级就给我创建了一个对象。我们通常使用构造函数注入的方式进行依赖的注入。
上文的代码就会变成
class Man
{
private readonly IPhone _phone;
public Man(IPhone phone)
{
_phone = phone;
}
}
假设这个时候你需要将手机换成B品牌,那么只需要再注入的地方传入B品牌的对象即可了。
容器
但是现在又出现了一个新的问题,假设说这个类有100个使用该接口的依赖,如果,我们是不是要在100个地方做这样的事情? 控制是反转了,依赖的创建也移交到了外部。现在的问题是依赖太多,我们需要一个地方统一管理系统中所有的依赖,这个时候,我们就使用容器进行集中的管理
容器负责两件事情:
- 绑定服务与实例之间的关系
- 获取实例,并对实例进行管理(创建与销毁)
使用
说了那么多,我们如何在.NET Core中使用我们的依赖注入呢?这里我们针对的是所有的.NET Core的应用,在.NET Core中依赖注入的核心分为两个组件:位于Microsoft.Extensions.DependencyInjection命名空间下的IServiceCollection和 IServiceProvider。
其中
- IServiceCollection 负责注册
- IServiceProvider 负责提供实例
在默认的容器ServiceCollection中有三个方法
- .AddTransient<I,C>()
- .AddSingleton<I,C>()
- .AddScoped<I,C>()
这里就不得不提一下我们依赖注入的三种生命周期了
- Singleton指的是单例模式,也就是说,在整个程序运转期间只会生成一次
- Transient指每一次GetService都会创建一个新的实例
- Scope指在同一个Scope内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)
我们可以尝试使用控制台项目来模拟依赖注入的原理,也就是说我们直接从容器获取我们对象实例,并且我们使用Guid进行唯一性的标记。
//创建三个代表不同生命周期的接口
interface IPhoneScope
{
Guid Guid { get; }
}
interface IPhoneSingleton
{
Guid Guid { get; }
}
interface IPhoneTransient
{
Guid Guid { get; }
}
//实现的类
class PhoneService:IPhoneScope,IPhoneSingleton,IPhoneTransient
{
public PhoneService()
{
this._guid = Guid.NewGuid();
}
public PhoneService(Guid guid)
{
this._guid = guid;
}
private Guid _guid;
public Guid Guid => this._guid;
}
然后,在我们的主函数中
namespace DI_AND_IOC
{
class Program
{
static void Main(string[] args)
{
//注入服务
var services = new ServiceCollection()
.AddScoped<IPhoneScope, PhoneService>()
.AddTransient<IPhoneTransient, PhoneService>()
.AddSingleton<IPhoneSingleton, PhoneService>();
//构造服务
var provider = services.BuildServiceProvider();
using (var scope = provider.CreateScope())
{
var p = scope.ServiceProvider;
var scopeobj1 = p.GetService<IPhoneScope>();
var transient1 = p.GetService<IPhoneTransient>();
var singleton1 = p.GetService<IPhoneSingleton>();
var scopeobj2 = p.GetService<IPhoneScope>();
var transient2 = p.GetService<IPhoneTransient>();
var singleton2 = p.GetService<IPhoneSingleton>();
Console.WriteLine(
$"scope1: {scopeobj1.Guid},\n" +
$"transient1: {transient1.Guid}, \n" +
$"singleton1: {singleton1.Guid}\n");
Console.WriteLine($"scope2: {scopeobj2.Guid}, \n" +
$"transient2: {transient2.Guid},\n" +
$"singleton2: {singleton2.Guid}\n");
}
//创建不同的scope
using (var scope = provider.CreateScope())
{
var p = scope.ServiceProvider;
var scopeobj3 = p.GetService<IPhoneScope>();
var transient3 = p.GetService<IPhoneTransient>();
var singleton3 = p.GetService<IPhoneSingleton>();
Console.WriteLine($"scope3: {scopeobj3.Guid}, \n" +
$"transient3: {transient3.Guid},\n" +
$"singleton3: {singleton3.Guid}");
}
}
}
}
你应该会得到类似以下的数据
scope1: 096d38e5-0c7b-4e50-9c79-241fb18a56ed,
transient1: 289ebd11-8159-4f22-b53e-ed738a317313,
singleton1: b453b7f5-3594-4b66-99c8-a72763abaa83
scope2: 096d38e5-0c7b-4e50-9c79-241fb18a56ed,
transient2: 212ad420-e54c-4dd6-9214-abe91aacdd9c,
singleton2: b453b7f5-3594-4b66-99c8-a72763abaa83
scope3: 688b6ffd-a8c1-47f4-a20a-872c2285d67c,
transient3: 3d09997d-fffb-43d1-9e53-ccf9771c819d,
singleton3: b453b7f5-3594-4b66-99c8-a72763abaa83
可以发现,singleton对象是不会发生改变的,而scope对象在创建新的scope之后就发生了改变,而transient对象每一次请求都在发生改变。
需要注意的是,在控制台项目使用容器服务需要引入 *** Microsoft.Extensions.DependencyInjection *** 程序集,你可以在引入中导入该dll
通过对注入服务的生命周期管控,在一些ASP.NET Core项目中,有些类(服务)有可能跨越了多个Action或者Controller,那么我们正确的使用生命周期,我们可以尽可能的节省内存,即能减少实例初始化的消耗。
在ASP.NET Core中的使用
在ASP.NET Core中,我们使用依赖注入非常的简单,在StartUp类中的ConfigureServices方法中已经为我们构建好了容器,我们只需要做类似于这样的操作
services.AddScoped<IPhoneScope, PhoneService>();
services.AddDbContext<DbContext>();
services.AddMVC();
如果你需要在控制器中注入服务,官方的推荐方案是使用构造函数注入
public IPhoneScope _ips;
public Controller(IPhoneScope ips)
{
_ips = ips;
}
特别的,你如果使用MVC的Razor页面进行注入的话,那么输入以下指令
@inject IPhoneScope ips
如果我的文章帮助了您,请您在github.NETCoreGuide项目帮我点一个star,在博客园中点一个关注和推荐。
.NET Core ASP.NET Core Basic 1-2 控制反转与依赖注入的更多相关文章
- .Net Core MVC 网站开发(Ninesky) 2.3、项目架构调整-控制反转和依赖注入的使用
再次调整项目架构是因为和群友dezhou的一次聊天,我原来的想法是项目尽量做简单点别搞太复杂了,仅使用了DbContext的注入,其他的也没有写接口耦合度很高.和dezhou聊过之后我仔细考虑了一下, ...
- ASP.NET Core快速入门学习笔记(第3章:依赖注入)
课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 任务16:介绍 1.依赖注入概念详解 从UML和软件建模来理解 从单元测试来理 ...
- .NET Core & ASP.NET Core 1.0在Redhat峰会上正式发布
众所周知,Red Hat和微软正在努力使.NET Core成为Red Hat企业版Linux (RHEL)系统上的一流开发平台选项.这个团队已经一起工作好几个月了,RHEL对.NET有许多需求.今天在 ...
- .NET Core & ASP.NET Core 1.0
.NET Core & ASP.NET Core 1.0在Redhat峰会上正式发布 众所周知,Red Hat和微软正在努力使.NET Core成为Red Hat企业版Linux (RHEL) ...
- Do You Kown Asp.Net Core -- Asp.Net Core 2.0 未来web开发新趋势 Razor Page
Razor Page介绍 前言 上周期待已久的Asp.Net Core 2.0提前发布了,一下子Net圈热闹了起来,2.0带来了很多新的特性和新的功能,其中Razor Page引起我的关注,作为web ...
- ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 配置 Entity Fram ...
- [.NET Core]ASP.NET Core中如何解决接收表单时的不支持的媒体类型(HTTP 415 Unsupported Media Type)错误呢?
[.NET Core]ASP.NET Core中如何解决接收表单时的不支持的媒体类型(HTTP 415 Unsupported Media Type)错误呢? 在ASP.NET Core应用程序中,接 ...
- ABP(现代ASP.NET样板开发框架)系列之6、ABP依赖注入
点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之6.ABP依赖注入 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...
- 解读ASP.NET 5 & MVC6系列(7):依赖注入
在前面的章节(Middleware章节)中,我们提到了依赖注入功能(Dependency Injection),ASP.NET 5正式将依赖注入进行了全功能的实现,以便开发人员能够开发更具弹性的组件程 ...
随机推荐
- 数组(ArrayPool数组池、Span<T>结构)
前言 如果需要使用相同的类型的多个对象,就可以使用集合和数组,这一节主要讲解数组,其中会重点涉及到Span<T>结构和ArrayPool数组池.我们也会先涉及到简单的数组.多维数组.锯齿数 ...
- myeclipse中更改默认jdk版本出错( Target is not a JDK root. System library was not found)
原因是我的本地jdk版本是9.0,将jdk版本更改至8.0即可导入成功. jdk9.0导入myeclipse中去会有此类问题的发生,因此没有必要使用最新的jdk版本.
- google、谷歌浏览截图
对于前端好用的浏览器---谷歌浏览器(没有插件)截取全屏很难受! 特备是前端,想截图下来,好好的量一下容器之前的尺寸(手动恼火) 对于程序员来说不一定需要插件,有很多大佬应该都知道, 小白记忆不好,每 ...
- springBoot综合开发
作者:纯洁的微笑出处:www.ityouknow.com 版权所有,欢迎保留原文链接进行转载:) 上篇文章介绍了Spring boot初级教程:spring boot(一):入门篇,方便大家快速入门. ...
- RobotFramework_2.新建项目、新建用例、运行用例和查看测试报告
RobotFramework的使用 新建一个项目 Robotframework-ride的界面 分了四个区域:菜单栏.工具栏.案例及资源区.工作区,如下图: 创建项目 首先,点击File-New Pr ...
- CentOS 配置阿里云 NTP 服务
NTP 是网络时间协议(Network Time Protocol),NTP 服务能保证服务器的本地时间与标准时间同步. ▶ 配置时区信息 1.删除系统里的当地时间链接 sudo rm /etc/lo ...
- Mac相关快捷键操作
拷贝: shift + option + 拖动拖动至目的地 创建快捷方式: option + command + 拖动至目的地
- Extjs的使用总结笔记
一:Extjs自带验证 1.alpha //只能输入字母,无法输入其他(如数字,特殊符号等) 2.alphanum//只能输入字母和数字,无法输入其他 3.email//email验证,要求的格式是& ...
- 【POJ - 1862】Stripies (贪心)
Stripies 直接上中文了 Descriptions 我们的化学生物学家发明了一种新的叫stripies非常神奇的生命.该stripies是透明的无定形变形虫似的生物,生活在果冻状的营养培养基平板 ...
- 后端小白的VUE入门笔记, 前端高能慎入
因为项目需要前后端分离,后端竟然不用控制view层了,页面的跳转后端不再干涉,(前端的vue经过打包后成了一张index.html) 后端只需要响应给前端json串就ok,其实这不是爽歪歪?但是觉得还 ...