文章首发地址

一、前言

在上一篇中,我们讲述了什么是控制反转(IoC)以及通过哪些方式实现的。这其中,我们明白了,控制反转(IoC) 是一种软件设计的模式,指导我们设计出更优良,更具有松耦合的程序,而具体的实现方式依赖注入依赖查找

在上篇实例中,我们通过日志的方式举例说明,其中通过代码创建了一个ILogger的接口,并实现接口实例,基于控制反转的模式,依赖的创建也移交到了外部,但是也发现存在了问题,如果类似存在这样多个接口和实现类,依赖太多,一一创建,没有统一的管理,这反而增加了实际工作量麻烦。

因此我们需要一个可以统一管理系统中所有的依赖的地方,因此,IoC容器诞生了。

容器负责两件事情:

  • 绑定服务与实例之间的关系
  • 获取实例,并管理实例对象生命周期(创建与销毁)

所以在这一篇中,我们主要讲述Asp.Net Core中内置的IoC容器。

二、说明

在Asp.Net Core中已经为我们集成提供了一个内置的IoC容器,我们可以看到在Startup.csConfigureServices中,涉及到依赖注入的核心组件,一个负责实例注册IServiceCollection和一个负责提供实例IServiceProvider

简单的说就是两步:1. 把实例注册到容器中;2. 从容器中获取实例

而在这其中,IServiceCollection为实现将开发者定义好的实例注册进去提供了三种方法

分别是:

AddTransientAddScopedAddSingleton

而这三种不同实例方法也对应的着三种不同的实例生命周期

三种不同的生命周期:

2.1 暂时性

AddTransient

每次在向服务容器进行请求时都会创建新的实例,这种生存期适合轻量级、 无状态的服务。

2.2 作用域内

AddScoped

在每次Web请求时被创建一次实例,生命周期横贯整次请求。

局部单例对象, 在某个局部内是同一个对象(作用域单例,本质是容器单例);一次请求内是一个单例对象,多次请求则多个不同的单例对象。

2.3 单例

AddSingleton

创建单例生命周期服务的情况如下:

  • 在首次请求它们时进行创建;
  • 或者在向容器直接提供实现实例时由开发人员进行创建。 很少用到此方法。

其后的每一个后续请求都使用同一个实例。如果开发者的应用需要单例服务情景,推荐的做法是交给服务容器来负责单例的创建和生命周期管理,而不是手动实现单例模式然后由开发者在自定义类中进行操作。

不要从单一实例解析指定了作用域的服务。 当处理后续请求时,它可能会导致服务处于不正确的状态。 可以从范围内或暂时性服务解析单一实例服务。

三、开始

3.1 接口

定义三个接口,分别测试Singleton,Scope,Transient三种,一个 TestService服务

    public interface ITransientService
{
string GetGuid();
}
    public interface IScopedService
{
string GetGuid();
}
    public interface ISingletonService
{
string GetGuid();
}
    public interface ITestService
{
public string GetSingletonID();
public string GetTransientID();
public string GetScopedID();
}

3.2 实现

根据上面定义的几种接口,并一一实现对应的接口


public class TransientService : ITransientService
{
public string OperationId { get; }
public TransientService()
{
OperationId = Guid.NewGuid().ToString()[^4..];
}
public string GetGuid()
{
return $"这是一个 Transient service : " + OperationId;
}
}
    public class ScopedService : IScopedService
{
public string OperationId { get; }
public ScopedService()
{
OperationId = Guid.NewGuid().ToString()[^4..];
} public string GetGuid()
{
return $"这是一个 scoped service : "+ OperationId;
}
}
    public class SingletonService : ISingletonService
{
public string OperationId { get; }
public SingletonService()
{
OperationId = Guid.NewGuid().ToString()[^4..];
} public string GetGuid()
{
return $"这是一个 Singleton service : " + OperationId;
}
}
    public class TestService : ITestService
{
private ITransientService _transientService;
private IScopedService _scopedService;
private ISingletonService _singletonService;
public TestService(ITransientService transientService, IScopedService scopedService, ISingletonService singletonService)
{
_transientService = transientService;
_scopedService = scopedService;
_singletonService = singletonService;
} public string GetSingletonID()
{
return _singletonService.GetGuid();
}
public string GetTransientID()
{
return _transientService.GetGuid();
}
public string GetScopedID()
{
return _scopedService.GetGuid();
}
}

3.3 注入

Startup.cs类文件ConfigureServices方法中,注入依赖

        public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddTransient<ITransientService, TransientService>();
services.AddSingleton<ISingletonService, SingletonService>();
services.AddScoped<IScopedService, ScopedService>();
services.AddScoped<ITestService, TestService>();
}

3.4 调用

定义一个控制器,实现调用

[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
private ITransientService _transientService;
private IScopedService _scopedService;
private ISingletonService _singletonService;
private ITestService _testService;
public TestController(ITransientService transientService, IScopedService scopedService, ISingletonService singletonService,
ITestService testService
)
{
_transientService = transientService;
_scopedService = scopedService;
_singletonService = singletonService; _testService = testService;
} [HttpGet]
public JsonResult Get()
{
var data1 = _transientService.GetGuid();
var data2 = _testService.GetTransientID(); var data3 = _scopedService.GetGuid();
var data4 = _testService.GetScopedID(); var data5 = _singletonService.GetGuid();
var data6 = _testService.GetSingletonID();
return new JsonResult(new {
data1,
data2,
data3 , data4,
data5,
data6,
});
}
}

在上面中我们了解到,注入的方式一般有三种,构造函数注入, 方法注入,属性注入,而在ASP.NET Core中自带的这个IoC容器,默认采用了构造函数注入的方式。

3.5 测试

启动运行项目,访问接口/Test

效果如下:

3.6 对比

对比两次的请求访问可以发现,上面我们一共得到了 4个Transient实例,2个Scope实例,1个Singleton实例。

在请求中,AddSingleton方式的id值相同;

AddScope方式两次请求之间不同,但同一请求内是相同的;

AddTransient方式在同一请求内的多次注入间都不相同。

3.7小结

通过上述的代码示例,也证实了之前的三种方式对应不同生命周期的说明。

暂时性(Transient) : 生命周期是每次获得对象都是一次新的对象,每一次都不一样。

作用域内(Scoped) : 生命周期是在每一次请求作用域内是同一个对象,非作用域内则是新的对象。

单例(Singletion) : 生命周期是这个服务启动后都是一个对象,也即是全局单例对象

四、其他Ioc容器

通过上述的了解,我们知道IoC容器是一个依赖注入框架,在.NET Core中也提供了内置的IoC容器,通过AddXXX方法来实例依赖对象,而在实际开发中,就是每一个实例都需要一个个的添加,这样的实现方式在小项目中还好,但是如果在复杂大型的项目中,就略向麻烦些,可能需要添加很多方法来实现,整体的可观性也不好。

为了达到可以简化我们工作量,应该采用批量注册,因此我们也可以引入其他的Ioc容器框架,实现更多的功能和扩展。

在平时开发中,常用的IoC框架有很多,而在这里我们选择用Autofac,这也是在.net下比较流行的,其他的框架不做说明,可自行查阅了解。

ASP.Net Core中使用Autofac 框架注入 (在后续篇章会具体说明)

五、思考

在实际的开发中,对于这几种生命周期,我们应该如何应用呢?

比如,在像DBContext这种实例,在实际开发中是用Transient 还是Scoped呢?

建议使用Scoped

因为有些对象在请求中可以需要用到多个方法或者多个Service、Repository的时候,为了减少实例初始化的消耗,实现事务功能,可以在整个请求的生命周期共用一个Scope。

其他的思考:

  1. ASP.NET Core中,默认采用了构造函数注入的方式,如果采用属性注入或者方法注入,又该怎么实现?
  2. 一个接口多种实现的时候,我们将多种实现都给注入进了依赖注入容器中,但是在服务调用的时候总是获取到最后注入的那个方法的实现,这时候就在想能不能实现动态的选择使用哪种实现呢?
  3. 一个作用域(Scoped)服务中注入一个瞬时(Transient)服务时,瞬时服务中的值还会每次都变化吗?
  4. 链式注入时,生存期的选择,三种注入方式的权重问题

以上的思考,大家可以说说自己的想法

在后续的篇章中,也会对这些问题,进行深入讨论说明。

六、总结

本篇主要介绍了什么是IoC容器,了解到它是DI构造函注入的框架,它管理着依赖项的生命周期以及映射关系,同时也介绍实践了在ASP.Net Core中,默认提供的内置IoC容器,以及它的实例注册方式和相应的生命周期。

好啦,这篇文章就先讲述到这里吧,在后续篇章中会对ASP.Net Core中使用Autofac 框架实践说明,希望对大家有所帮助。

如果有不对的或不理解的地方,希望大家可以多多指正,提出问题,一起讨论,不断学习,共同进步。

ASP.NET Core依赖注入初识与思考的更多相关文章

  1. # ASP.NET Core依赖注入解读&使用Autofac替代实现

    标签: 依赖注入 Autofac ASPNETCore ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Aut ...

  2. 实现BUG自动检测 - ASP.NET Core依赖注入

    我个人比较懒,能自动做的事绝不手动做,最近在用ASP.NET Core写一个项目,过程中会积累一些方便的工具类或框架,分享出来欢迎大家点评. 如果以后有时间的话,我打算写一个系列的[实现BUG自动检测 ...

  3. [译]ASP.NET Core依赖注入深入讨论

    原文链接:ASP.NET Core Dependency Injection Deep Dive - Joonas W's blog 这篇文章我们来深入探讨ASP.NET Core.MVC Core中 ...

  4. asp.net core 依赖注入几种常见情况

    先读一篇注入入门 全面理解 ASP.NET Core 依赖注入, 学习一下基本使用 然后学习一招, 不使用接口规范, 直接写功能类, 一般情况下可以用来做单例. 参考https://www.cnblo ...

  5. ASP.NET Core依赖注入——依赖注入最佳实践

    在这篇文章中,我们将深入研究.NET Core和ASP.NET Core MVC中的依赖注入,将介绍几乎所有可能的选项,依赖注入是ASP.Net Core的核心,我将分享在ASP.Net Core应用 ...

  6. 自动化CodeReview - ASP.NET Core依赖注入

    自动化CodeReview系列目录 自动化CodeReview - ASP.NET Core依赖注入 自动化CodeReview - ASP.NET Core请求参数验证 我个人比较懒,能自动做的事绝 ...

  7. ASP.NET Core 依赖注入最佳实践——提示与技巧

    在这篇文章,我将分享一些在ASP.NET Core程序中使用依赖注入的个人经验和建议.这些原则背后的动机如下: 高效地设计服务和它们的依赖. 预防多线程问题. 预防内存泄漏. 预防潜在的BUG. 这篇 ...

  8. ASP.NET Core依赖注入最佳实践,提示&技巧

    分享翻译一篇Abp框架作者(Halil İbrahim Kalkan)关于ASP.NET Core依赖注入的博文. 在本文中,我将分享我在ASP.NET Core应用程序中使用依赖注入的经验和建议. ...

  9. ASP.NET Core依赖注入解读&使用Autofac替代实现【转载】

    ASP.NET Core依赖注入解读&使用Autofac替代实现 1. 前言 2. ASP.NET Core 中的DI方式 3. Autofac实现和自定义实现扩展方法 3.1 安装Autof ...

随机推荐

  1. Apple iPhone 12 Pro 数据迁移方式 All In One

    Apple iPhone 12 Pro 数据迁移方式 All In One iPhone 12 Pro https://mp.weixin.qq.com/s/US1Z_69zVQIhV-cNW1E6A ...

  2. js 小数转整数,避免精度损失 bug

    js 小数转整数,避免精度损失 bug const arr = [ 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01 ]; // ( ...

  3. uniapp 万年历

    大量代码来至这里 <template> <view class="calendar-main"> <!-- 当前年月 --> <view ...

  4. c++ 使用PID获取可执行文件路径

    注意看备注 https://docs.microsoft.com/en-us/windows/win32/api/psapi/nf-psapi-getmodulefilenameexa #includ ...

  5. 适合Linux嵌入式项目的代码构建与依赖管理工具——cazel

    前言 我们知道,现在有很多流行的优秀代码构建工具,如CMake.jetkins.bazel等.这些不同的构建工具在其应用的领域起到了举足轻重的作用. 但是,如果仔细研究就会发现,在嵌入式领域,构建工具 ...

  6. Python爬虫_糗事百科

    本爬虫任务: 爬虫糗事百科网站(https://www.qiushibaike.com/)--段子版块中所有的[段子].[投票数].[神回复]等内容 步骤: 通过翻页寻找url规律,构造url列表 查 ...

  7. ffmpeg:为视频添加静态水印

    在ffmpeg中,添加水印需要用overlay滤镜,这是一个复杂滤镜,因为它需要两个输入,默认第一个输入是主画面,第二输入为水印,先执行一个简单的看看. 下面有两个文件,一个是可爱的大雄兔,一个是可爱 ...

  8. 用OkHttpGo和FastJson获取OneNET云平台数据(解析嵌套数组)

    JSON数据格式有两种,一种是 { } 大括号表示的JSON对象,一种是 [ ] 中括号表示的JSON数组.从OneNET获取到的数组是这样的,并用Json解析网址查看https://jsonform ...

  9. 页面强制重新加载js的办法

    1:线上强制重新加载js的办法 js后缀?v1.0 2:开发环境强制重新加载js的办法?now=Date.now() 3:开发环境强制重新加载js的办法F12进入调试页面选择network下单 dis ...

  10. Django自学计划之集装箱货物运输物流仓储一站式ERP系统

    业余开始学习时间:2018年1月 业余学习时间段:每天下班晚饭后时间+无事的星期六和星期天+上班时的空闲时间 自学目标: 1.我们要用管理的思维来写我们的系统! 2.我们要用我们的ERP系统帮助中小集 ...