.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正式将依赖注入进行了全功能的实现,以便开发人员能够开发更具弹性的组件程 ...
随机推荐
- springboot集成shiro集成mybatis-plus、redis、quartz定时任务
完整项目代码位于码云上,点击获取:Git地址 主要介绍一下重点配置地方: 一.application.yml文件 server: port: 8084 servlet: context-path: / ...
- java读写文件小心缓存数组
一般我们读写文件的时候都是这么写的,看着没问题哈. public static void main(String[] args) throws Exception { FileInputStr ...
- Apache和Spring提供的StopWatch执行时间监视器
相关阅读 [小家java]java5新特性(简述十大新特性) 重要一跃 [小家java]java6新特性(简述十大新特性) 鸡肋升级 [小家java]java7新特性(简述八大新特性) 不温不火 [小 ...
- 【iOS】The filename 未命名.ipa in the package contains an invalid character(s)
提交 APP 到苹果官网审核时遇到了这个问题,如图: 其实就是不支持中文,随便换个英文名就行了. 参考:http://blog.csdn.net/u011439689/article/details/ ...
- Core CLR Host 源码简单分析
在定制 CLR Host的时候,可以通过调用如下代码,来获取当前需要被宿主的程序调用入口: hr = Host->CreateDelegate( domainId, L"Main,Ve ...
- golang文档、中文、学习文档
Golang中文文档地址 http://zh-golang.appspot.com/doc/ Golang非英文文档地址: https://github.com/golang/go/wiki/NonE ...
- 用scrapy爬取搜狗Lofter图片
用scrapy爬取搜狗Lofter图片 # -*- coding: utf-8 -*- import json import scrapy from scrapy.http import Reques ...
- 一文搞懂Python迭代器和生成器
很多童鞋搞不懂python迭代器和生成器到底是什么?它们之间又有什么样的关系? 这篇文章就是要用最简单的方式让你理解Python迭代器和生成器! 1.迭代器和迭代过程 维基百科解释道: 在Python ...
- idea 新建不了servlet文件 方法(1)
在pem.xml中添加较新版本的servletapi包 <dependency> <groupId>javax.servlet</groupId> <arti ...
- 3PHP如何用PDO的连接方式方式导出mysql数据
首先连接mysql,具体看上一篇 接下来在try{}中加入以下代码 $query="select * from 你的数据表名称" //$query的内容给个SQL ...