.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正式将依赖注入进行了全功能的实现,以便开发人员能够开发更具弹性的组件程 ...
随机推荐
- 在父页面用Iframe加载子页面时,将父页面的title替换成子页面title
报告管理
- linux初学者-进程篇
linux初学者-进程篇 不管是windows还是linux,都有进程,那么什么是进程呢?进程就是cpu未完成的工作.下面会介绍一些关于系统中进程的查看以及管理的方法. 1.命令 1.1.命令使用 查 ...
- Jenkins-slave分布式环境构建与并行WebUi自动化测试项目
前言 之前搭建过selenium grid的分布式环境,今天我们再来搭建一次Jenkins的分布式环境:jenkins-slave Jenkins的Master-Slave分布式架构主要是为了解决Je ...
- PageHelper分页实战(SSM整合)
步骤一:引入SSM相关的jar包,包列表如下: 步骤二:创建或修改配置文件,配置文件清单如下: applicationContext.xml <?xml version="1.0&qu ...
- Skier 游戏
# Listing_10-1.py # Copyright Warren Sande, 2009 # Released under MIT license http://www.opensource. ...
- 【JDK】JDK源码分析-Collection
Java 集合框架(Java Collections Framework, JCF)包含很多平时开发中的常用类,例如 List.Set.ArrayList.HashMap.HashSet 等,因此打算 ...
- CodeForces 372 A. Counting Kangaroos is Fun
题意,有n只袋鼠,没每只袋鼠有个袋子,大小为si,一个袋鼠可以进入另外一个袋鼠的袋子里面,当且仅当另一个袋鼠的袋子是他的二倍或二倍一上,然后中国袋鼠就是不可见的,不能出现多个袋鼠嵌套的情况.让你求最少 ...
- 五、Python基础(2)
五,Python基础(2) 1.数据类型基础 (一)什么是数据类型? 用于区分变量值的不同类型. (二)为何对数据分类? 针对不同状态就应该用不同类型的数据去标识. (三)数据类型分类 1.数字类型 ...
- 8、大型项目的接口自动化实践记录----DB分别获取预期结果、实际结果
上一篇实现数据分离升级版--从DB获取数据,以及对应的请求实现,作为一个case,还缺少了预期结果与实际结果的获取及对比.因为前面的文章已经说过接口返回值的获取及对比,所以这篇不说这块了,这篇说一下D ...
- MariaDB 修改存储路径后启动失败问题解决
修改 MariaDB 路径到 home 路径下, 执行 systemctl start mariadb 启动MariaDB 时,报错提示: Job for mariadb.service failed ...