2021-04-02

ASP.NET Core依赖注入初识与思考

文章首发地址

一、前言

在上一篇中,我们讲述了什么是控制反转(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 框架实践说明,希望对大家有所帮助。

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









原文转载:http://www.shaoqun.com/a/659732.html

跨境电商:https://www.ikjzd.com/

bol:https://www.ikjzd.com/w/291

wish:https://www.ikjzd.com/w/105


文章首发地址一、前言在上一篇中,我们讲述了什么是控制反转(IoC)以及通过哪些方式实现的。这其中,我们明白了,控制反转(IoC)是一种软件设计的模式,指导我们设计出更优良,更具有松耦合的程序,而具体的实现方式有依赖注入和依赖查找。在上篇实例中,我们通过日志的方式举例说明,其中通过代码创建了一个ILogger的接口,并实现接口实例,基于控制反转的模式,依赖的创建也移交到了外部,但是也发现存在了问题,
夸克:https://www.ikjzd.com/w/1237
启明星:https://www.ikjzd.com/w/1436
promoted:https://www.ikjzd.com/w/971
亚马逊黄金日购物意愿降低?电子商务整体搜索量下降84%:https://www.ikjzd.com/home/132109
贱闺蜜大闹婚礼抢走我老公:http://www.30bags.com/m/a/252181.html
乡村美娘子对我实在太好了 口述我和农村小媳妇的那些事:http://www.30bags.com/m/a/250728.html

No comments:

Post a Comment