.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,在博客园中点一个关注和推荐。

Github

BiliBili主页

WarrenRyan'sBlog

博客园

.NET Core ASP.NET Core Basic 1-2 控制反转与依赖注入的更多相关文章

  1. .Net Core MVC 网站开发(Ninesky) 2.3、项目架构调整-控制反转和依赖注入的使用

    再次调整项目架构是因为和群友dezhou的一次聊天,我原来的想法是项目尽量做简单点别搞太复杂了,仅使用了DbContext的注入,其他的也没有写接口耦合度很高.和dezhou聊过之后我仔细考虑了一下, ...

  2. ASP.NET Core快速入门学习笔记(第3章:依赖注入)

    课程链接:http://video.jessetalk.cn/course/explore 良心课程,大家一起来学习哈! 任务16:介绍 1.依赖注入概念详解 从UML和软件建模来理解 从单元测试来理 ...

  3. .NET Core & ASP.NET Core 1.0在Redhat峰会上正式发布

    众所周知,Red Hat和微软正在努力使.NET Core成为Red Hat企业版Linux (RHEL)系统上的一流开发平台选项.这个团队已经一起工作好几个月了,RHEL对.NET有许多需求.今天在 ...

  4. .NET Core & ASP.NET Core 1.0

    .NET Core & ASP.NET Core 1.0在Redhat峰会上正式发布 众所周知,Red Hat和微软正在努力使.NET Core成为Red Hat企业版Linux (RHEL) ...

  5. 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 ...

  6. ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程

    原文:ASP.NET Core 配置 Entity Framework Core - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 配置 Entity Fram ...

  7. [.NET Core]ASP.NET Core中如何解决接收表单时的不支持的媒体类型(HTTP 415 Unsupported Media Type)错误呢?

    [.NET Core]ASP.NET Core中如何解决接收表单时的不支持的媒体类型(HTTP 415 Unsupported Media Type)错误呢? 在ASP.NET Core应用程序中,接 ...

  8. ABP(现代ASP.NET样板开发框架)系列之6、ABP依赖注入

    点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之6.ABP依赖注入 ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)” ...

  9. 解读ASP.NET 5 & MVC6系列(7):依赖注入

    在前面的章节(Middleware章节)中,我们提到了依赖注入功能(Dependency Injection),ASP.NET 5正式将依赖注入进行了全功能的实现,以便开发人员能够开发更具弹性的组件程 ...

随机推荐

  1. linux初学者-DNS集群篇

    linux初学者-DNS集群篇 DNS服务器一般在使用时,为了缓解服务器的压力,多使用一个主DNS服务器,多个副DNS服务器,这些DNS服务器就组成了一个DNS集群. 在DNS主服务器配置好后,需要另 ...

  2. 基于SDN网络的负载均衡研究与实现

    为什么需要软件定义网络 1.网络缺乏可扩展性,创新正在停滞不前.   我们最新的研究发现,几乎每两个组织中就有一个认为需要将网络功能扩展为采用SDN的主要业务触发因素,而不是其他催化剂.这一统计数据一 ...

  3. javascript获取指定区间范围随机数

    //获取指定区间范围随机数,包括lowerValue和upperValuefunction randomFrom(lowerValue,upperValue){    return Math.floo ...

  4. 【iOS】No suitable application records found

    昨天提交 Apple 审核时遇到这个问题,如图: 原来是还没在 iTunes Connect 创建 APP ... 一时着急大意了…… 后来想想还真是脑子一时没反应过来……

  5. Vue组件间通信-Vuex

    上回说到Vue组件间通讯,最后留了一个彩蛋~~~Vuex.Vuex是另一种组件通讯的方法,这节来说说Vuex(store仓库). 首先Vuex需要安装,安装的方式有很多,在这里就不一一细说了.我是通过 ...

  6. oracle-11g2下载安装笔记

    一.下载链接地址 http://download.oracle.com/otn/nt/oracle11g/112010/win64_11gR2_database_1of2.zip http://dow ...

  7. HDP Hive性能调优

    (官方文档翻译整理及总结) 一.优化数据仓库 ① Hive LLAP  是一项接近实时结果查询的技术,可用于BI工具以及网络看板的应用,能够将数据仓库的查询时间缩短到15秒之内,这样的查询称之为Int ...

  8. jquery 动态载入页面,并且保证 url 变动

    最近做一个新的项目,项目页头,导航,页尾是不变的,只有中间部分是通过加载其他页面,达到内容刷新的. 大概结构如下, 要求, 1. 正文部分可以通过加载一个页面达到刷新效果 2. 保留加载的页面 url ...

  9. 【Java例题】6.1 进制转换

    1.进制转换.输入一个某种进制的整数,将其转换为其它进制的整数.说明:仅考虑十.二.八和十六进制. package chapter6; import java.util.*; public class ...

  10. 算法与数据结构基础 - 位运算(Bit Manipulation)

    位运算基础 说到与(&).或(|).非(~).异或(^).位移等位运算,就得说到位运算的各种奇淫巧技,下面分运算符说明. 1. 与(&) 计算式 a&b,a.b各位中同为 1 ...