构建ASP.NET Core应用程序的时候,依赖注入已成为了.NET Core的核心,这篇文章,我们理一理依赖注入的使用方法。

不使用依赖注入

首先,我们创建一个ASP.NET Core Mvc项目,定义个表达的爱服务接口,中国小伙类实现这个类如下:

public interface ISayLoveService
{
string SayLove();
} public class CNBoyService : ISayLoveService
{
public string SayLove()
{
return "安红,我喜欢你";
}
}

在LoveController 控制器中调用 ISayLoveService的SayLove方法。

public class LoveController : Controller

    {
private ISayLoveService loveService;
public IActionResult Index()
{
loveService = new CNBoyService(); //中国小伙对安红的表达
ViewData["SayLove"] = loveService.SayLove();
return View();
}
}

输出如图:

小结:LoveController控制器调用ISayLoveService服务的SayLove方法;我们的做法,直接在控制器去new CNBoyService()实例对象,也就是LoveController依赖ISayLoveService类。

思考:能不能有种模式,new实例不要在使用的时候进行创建,而是在外部或者有一个容器进行管理;这不就是ioc思想吗?好处,代码的解耦、代码更好的维护等等。

使用依赖注入

上面的疑惑,答案是肯定的,有!并且ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式(当然也可以兼容第三方)。我们还使用上面的代码,

服务注册

在Startup类ConfigureServices方法中注册服务容器中的依赖关系

  public void ConfigureServices(IServiceCollection services)

        {
services.AddSingleton<ISayLoveService, CNBoyService>();
services.AddControllersWithViews();
}

在LoveControlle控制器中,通过构造函数注入

 private readonly  ISayLoveService loveService;  
public LoveController(ISayLoveService loveService)
{ this.loveService = loveService;
} public IActionResult Index()
{

ViewData["SayLove"] = loveService.SayLove(); return View();
}

LoveController 正在将ISayLoveService作为依赖项注入其构造函数中,然后在Index方法中使用它。

推荐:

  • 将注入的依赖项分配给只读字段/属性(以防止在方法内部意外为其分配另一个值)。

  • 使用接口或基类抽象化依赖关系实现。

小结:在控制器中,还有几种使用如:[FromServices] 标签 、 HttpContext.RequestServices.GetService<T>();我们发现可以使用ASP.NET Core 提供了一个内置的服务容器 IServiceProvider。服务只需要在Startup.ConfigureServices 方法中注册,然后在运行时将服务注入 到使用它的类的构造函数中。 框架负责创建依赖关系的实例,并在不再需要时对其进行处理。

思考:服务注册的时候使用的是 AddSingleton,如services.AddSingleton<ISayLoveService, CNBoyService>();还有其他的吗?

服务生命周期

服务注册的时候,ASP.NET Core支持指定三种生命周期如:

  1. Singleton 单例

  2. Scoped 范围

  3. Transient 短暂的

Singleton 仅创建一个实例。该实例在需要它的所有组件之间共享。因此始终使用同一实例。

Scoped 每个范围创建一个实例。在对应用程序的每个请求上都会创建一个范围,因此每个请求将创建一次注册为Scoped的任何组件。

Transient 在每次被请求时都会创建,并且永不共享。

为了能够更好的裂解生命周期的概念,我们把上面代码稍作改动,做一个测试:

ISayLoveService 新增个属性LoveId,类型为guid,

public interface ISayLoveService
{
Guid LoveId { get; }
string SayLove();
}
public interface ITransientSayLoveService : ISayLoveService
{
}
public interface IScopedSayLoveService : ISayLoveService
{
}
public interface ISingletonSayLoveService : ISayLoveService
{
}
public interface ISingletonInstanceSayLoveService : ISayLoveService
{
}

BoyService也很简单,在构造函数中传入一个Guid,并对它进行赋值。

public class BoyService : ITransientSayLoveService,
IScopedSayLoveService,
ISingletonSayLoveService,
ISingletonInstanceSayLoveService
{
public BoyService():this(Guid.NewGuid()) { }
public BoyService(Guid id)
{
LoveId = id;
}
public Guid LoveId { get; private set; } public string SayLove()
{
return LoveId.ToString();
}
}

每个实现类的构造函数中,我们都产生了一个新的guid,通过这个GUID,我们可以判断这个类到底重新执行过构造函数没有.

服务注册代码如下:

   public void ConfigureServices(IServiceCollection services)
{
//生命周期设置为Transient,因此每次都会创建一个新实例。
services.AddTransient<ITransientSayLoveService, BoyService>();
services.AddScoped<IScopedSayLoveService, BoyService>();
services.AddSingleton<ISingletonSayLoveService, BoyService>();
services.AddSingleton<ISingletonInstanceSayLoveService>(new BoyService(Guid.Empty)); services.AddControllersWithViews();
}

在LifeIndex方法中多次调用ServiceProvider的GetService方法,获取到的都是同一个实例。

  public IActionResult LifeIndex()
{
ViewData["TransientSayLove1"] = HttpContext.RequestServices.GetService<ITransientSayLoveService>().SayLove();
ViewData["ScopedSayLove1"] = HttpContext.RequestServices.GetService<IScopedSayLoveService>().SayLove();
ViewData["SingletonSayLove1"] = HttpContext.RequestServices.GetService<ISingletonSayLoveService>().SayLove();
ViewData["SingletonInstanceSayLove1"] = HttpContext.RequestServices.GetService<ISingletonInstanceSayLoveService>().SayLove();
//同一个HTTP请求 ,在从容器中获取一次
ViewData["TransientSayLove2"] = HttpContext.RequestServices.GetService<ITransientSayLoveService>().SayLove();
ViewData["ScopedSayLove2"] = HttpContext.RequestServices.GetService<IScopedSayLoveService>().SayLove();
ViewData["SingletonSayLove2"] = HttpContext.RequestServices.GetService<ISingletonSayLoveService>().SayLove();
ViewData["SingletonInstanceSayLove2"] = HttpContext.RequestServices.GetService<ISingletonInstanceSayLoveService>().SayLove(); return View();
}

我们编写view页面,来展示这些信息如下:

@{
ViewData["Title"] = "LifeIndex";
} <div class="row">
<div class="panel panel-default">
<div class="panel-heading">
<h2 class="panel-title">Operations</h2>
</div>
<div class="panel-body">
<h3>获取第一次</h3>
<dl>
<dt>Transient1</dt>
<dd>@ViewData["TransientSayLove1"] </dd>
<dt>Scoped1</dt>
<dd>@ViewData["ScopedSayLove1"]</dd>
<dt>Singleton1</dt>
<dd>@ViewData["SingletonSayLove1"] </dd>
<dt>Instance1</dt>
<dd>@ViewData["SingletonInstanceSayLove1"]</dd>
</dl>
<h3>获取第二次</h3>
<dl>
<dt>Transient2</dt>
<dd>@ViewData["TransientSayLove2"]</dd>
<dt>Scoped2</dt>
<dd>@ViewData["ScopedSayLove2"]</dd>
<dt>Singleton2</dt>
<dd>@ViewData["SingletonSayLove2"]</dd>
<dt>Instance2</dt>
<dd>@ViewData["SingletonInstanceSayLove2"]</dd>
</dl>
</div>
</div>
</div>

运行代码第一次输出:

我们发现,在一次请求中,发现单例、范围的生命周期的guid 没有变化,说明分别用的是同一个对象,而瞬态guid不同,说明对象不是一个。

刷新之后,查看运行效果

我们发现通过刷新之后,单例模式的guid还是跟首次看到的一样,其他的都不同;

总结:如果您将组件A注册为单例,则它不能依赖已注册“作用域”或“瞬态”生存期的组件。一般而言:组件不能依赖寿命短于其寿命的组件。如果默认的DI容器不能满足项目需求,可以替换成第三方的如功能强大的Autofac。

.Net Core3.0依赖注入DI的更多相关文章

  1. Yii2.0 依赖注入(DI)和依赖注入容器的原理

    依赖注入和依赖注入容器 为了降低代码耦合程度,提高项目的可维护性,Yii采用多许多当下最流行又相对成熟的设计模式,包括了依赖注入(Denpdency Injection, DI)和服务定位器(Serv ...

  2. 05、NetCore2.0依赖注入(DI)之Web应用启动流程管理

    05.NetCore2.0依赖注入(DI)之Web应用启动流程管理 在一个Asp.net core 2.0 Web应用程序中,启动过程都做了些什么?NetCore2.0的依赖注入(DI)框架是如何管理 ...

  3. 06、NetCore2.0依赖注入(DI)之整合Autofac

    06.NetCore2.0依赖注入(DI)之整合Autofac 除了使用NetCore2.0系统的依赖注入(DI)框架外,我们还可以使用其他成熟的DI框架,如Autofac.Unity等.只要他们支持 ...

  4. 07、NetCore2.0依赖注入(DI)之生命周期

    07.NetCore2.0依赖注入(DI)之生命周期 NetCore2.0依赖注入框架(DI)是如何管理注入对象的生命周期的?生命周期有哪几类,又是在哪些场景下应用的呢? -------------- ...

  5. ADO.NET .net core2.0添加json文件并转化成类注入控制器使用 简单了解 iTextSharp实现HTML to PDF ASP.NET MVC 中 Autofac依赖注入DI 控制反转IOC 了解一下 C# AutoMapper 了解一下

    ADO.NET   一.ADO.NET概要 ADO.NET是.NET框架中的重要组件,主要用于完成C#应用程序访问数据库 二.ADO.NET的组成 ①System.Data  → DataTable, ...

  6. 控制反转(Ioc)和依赖注入(DI)

    控制反转IOC, 全称 “Inversion of Control”.依赖注入DI, 全称 “Dependency Injection”. 面向的问题:软件开发中,为了降低模块间.类间的耦合度,提倡基 ...

  7. 控制反转IOC与依赖注入DI【转】

    转自:http://my.oschina.net/1pei/blog/492601 一直对控制反转.依赖注入不太明白,看到这篇文章感觉有点懂了,介绍的很详细. 1. IoC理论的背景我们都知道,在采用 ...

  8. 依赖注入(DI)和Ninject

    [ASP.NET MVC 小牛之路]04 - 依赖注入(DI)和Ninject 本文目录: 1.为什么需要依赖注入 2.什么是依赖注入 3.使用NuGet安装库 4.使用Ninject的一般步骤 5. ...

  9. iOS控制反转(IoC)与依赖注入(DI)的实现

    背景 最近接触了一段时间的SpringMVC,对其控制反转(IoC)和依赖注入(DI)印象深刻,此后便一直在思考如何使用OC语言较好的实现这两个功能.Java语言自带的注解特性为IoC和DI带来了极大 ...

随机推荐

  1. Mysql学习笔记整理之引擎

    mysql的引擎: myisam引擎 Mysql 5.5之前默认的存储引擎 数据.索引分别存储 (数据物理磁盘---索引物理磁盘) .MYD 存储数据                      表级索 ...

  2. 首次GitHub千星项目提交维护成功 留念

    现在有点激动,可能有混乱的地方.请大家见谅. 一直觉得千星项目,对我来说是一个遥不可及的地方.没想到第一次在GitHub上 提交Pull Request 就成功了,并且是一个千星项目. 虽然 只是提出 ...

  3. 转:sqlserver 存储毫秒23:59:59.999变成第二天00:00:00.000

    因为,在SQL SERVER中DATETIME表示的时间为00:00:00到23:59:59.997,它的时间精度为1/300秒,在使用时会舍入到舍入到 .000..003 或 .007 秒三个增量. ...

  4. zookeeper特性与节点说明

    一.zookeeper概要.背景及作用 zookeeper产生背景: 项目从单体到分布式转变之后,将会产生多个节点之间协同的问题.如: 每天的定时任务由谁哪个节点来执行? RPC调用时的服务发现? 如 ...

  5. MySQL数据库忘记密码怎么办?

    忘记MySQL数据库密码就进不去数据库,也就无法修改密码,解决方法如下: 1:打开cmd命令符,先关闭正在运行的数据库,输入如下命令: 2:打开mysql.exe和mysqld.exe所在的文件夹,复 ...

  6. mybatis 启用延迟加载和按需加载配置

    启用延迟加载和按需加载 Mybatis配置文件中通过两个属性lazyLoadingEnabled和aggressiveLazyLoading来控制延迟加载和按需加载. lazyLoadingEnabl ...

  7. springboot jpa使用

    1.添加pom依赖: <dependency> <groupId>org.springframework.boot</groupId> <artifactId ...

  8. Angular 页面初始化动画

    用于进入组件前的加载动画 第一步:index.html 定义动画模板和样式 // 样式 <style type="text/css">.preloader { posi ...

  9. mysql执行过程以及顺序

    前言:mysql在我们的开发中基本每天都要面对的,作为开发中的数据中间件,mysql承担者存储数据和读写数据的职责.因为学习和了解mysql是至关重要的,那么当我们在客户端发起一个sql到出现详细的查 ...

  10. 五 mysql之多表查询

    目录 一 介绍 二 多表连接查询 1.交叉连接:不适用任何匹配条件.生成笛卡尔积 2.内连接:只连接匹配的行 3 .外链接之左连接:优先显示左表全部记录 4 .外链接之右连接:优先显示右表全部记录 5 ...