文章首发地址

一、前言

在上一篇中,我们讲述了什么是控制反转(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. fullstack web projects in action

    fullstack web projects in action web 全栈项目实战 Angular 全栈 Angular + TypeScript + Nest.js + PostgreSQL + ...

  2. free online markdown editor

    free online markdown editor markdown https://blog.csdn.net/xgqfrms/article/details/50129317 In-brows ...

  3. React Hooks 内部实现原理

    React Hooks 内部实现原理 源码分析 // 链表 React Hooks 原理剖析 refs https://reactjs.org/docs/hooks-intro.html https: ...

  4. node.js & ORM & ODM

    node.js & ORM & ODM ODM & NoSQL Object Data Modeling 对象数据模型 Object Document Mapping 对象文档 ...

  5. react slot component with args

    react slot component with args how to pass args to react props child component https://codesandbox.i ...

  6. NGK推出SPC算力币,开启算力新玩法!

    这两天,NGK公链再度上了热搜.因为既成功的打造DeFi生态以后,NGK又将目光对准了算力市场.试图通过算力代币化,让NGK算力持有者可以获得算力代币,同时,如果不想要了,算力持有者也可以抛售代币. ...

  7. DS线段树优化最短路&&01bfs浅谈

    1简介 为什么需要?原因很简单,当需要有大量的边去连时,用线段树优化可以直接用点连向区间,或从区间连向点,或从区间连向区间,如果普通连边,复杂度是不可比拟的.下面简单讲解一下线段树(ST)优化建图. ...

  8. 干货满满-原来这才是hooks-React Hooks使用心得

    序言 ---最后有招聘信息哦-React是一个库,它不是一个框架.用于构建用户界面的Javascript库.这里大家需要认识这一点.react的核心在于它仅仅是考虑了如何将dom节点更快更好更合适的渲 ...

  9. EurekaServer源码分析

    Eureka Server功能 接受服务注册 接受服务心跳 服务剔除 服务下线 集群同步 获取注册表中服务实例信息 需要注意的是,Eureka Server同时也是一个Eureka Client,在不 ...

  10. java 给时间增加时间得到一个新的时间(日期)

    SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd") LocalDate expirationDate String exp ...