Asp.Net Core中服务的生命周期选项区别与用法详解

 更新时间:2021年9月22日 10:02  点击:2143

前言

最近在做一个小的Demo中,在一个界面上两次调用视图组件,并且在视图组件中都调用了数据库查询,结果发现,一直报错,将两个视图组件的调用分离,单独进行,却又是正常的,寻找一番,发现是配置依赖注入服务时,对于服务的生命周期没有配置得当导致,特此做一次实验来认识三者之间(甚至是四者之间的用法及区别)。

本文demo地址(具体见WebApi控制器中):https://gitee.com/530521314/koInstance.git (本地下载)

 一、服务的生命周期

在Asp.Net Core中,内置容器负责管理服务的生命周期,从被依赖注入容器创建开始,等我们调用完服务时,到容器释放该服务的所有实力为止,有几种形式表现:

  1、Transient:每次请求服务时,都会创建一个新实例,这种生命周期适合用于轻量级服务(如Repository和ApplicationService服务)。

  2、Scoped:为每个HTTP请求创建一个实例,生命周期将横贯整次请求。

  3、SingleTon:在第一次请求服务时,为该服务创建一个实例,之后每次请求将会使用第一次创建好的服务。

  4、Instance:与SingleTon类似,但在应用程序启动时会将该实例注册到容器中,可以理解为比SingleTon还早存在。

应用程序中相关服务的控制生命周期的方法时通过相应的Add*指定,如下三种,当然还可以通过扩展方法来简化ConfigurationServices方法中所见的代码数量。

services.AddTransient<IApplicationService, ApplicationService>();
services.AddScoped<IApplicationService, ApplicationService>();
services.AddSingleton<IApplicationService, ApplicationService>();

二、代码设计服务生命周期

首先设计一些服务相关的操作接口

public interface IOperation
 {
 Guid GetGuid();
 }

 public interface IOperationTransient: IOperation
 {

 }

 public interface IOperationScoped : IOperation
 {

 }

 public interface IOperationSingleton : IOperation
 {
 
 }

 public interface IOperationInstance : IOperation
 {
 
 }

基础服务接口

其次对这些操作类予以实现并生成相关服务

/// <summary>
 /// 常规服务
 /// </summary>
 public class Operation : IOperation
 {
 private readonly Guid _guid;

 public Operation()
 {
 _guid = Guid.NewGuid();
 }

 public Operation(Guid guid)
 {
 _guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
 }

 public Guid GetGuid()
 {
 return _guid;
 }
 }

 /// <summary>
 /// 瞬时服务
 /// </summary>
 public class OperationTransient : IOperationTransient
 {
 private readonly Guid _guid;

 public OperationTransient()
 {
 _guid = Guid.NewGuid();
 }

 public OperationTransient(Guid guid)
 {
 _guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
 }

 public Guid GetGuid()
 {
 return _guid;
 }
 }

 /// <summary>
 /// 单次请求内服务固定
 /// </summary>
 public class OperationScoped : IOperationScoped
 {
 private readonly Guid _guid;

 public OperationScoped()
 {
 _guid = Guid.NewGuid();
 }

 public OperationScoped(Guid guid)
 {
 _guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
 }

 public Guid GetGuid()
 {
 return _guid;
 }
 }


 /// <summary>
 /// 所有请求内固定服务
 /// </summary>
 public class OperationSingleton : IOperationSingleton
 {
 private readonly Guid _guid;

 public OperationSingleton()
 {
 _guid = Guid.NewGuid();
 }

 public OperationSingleton(Guid guid)
 {
 _guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
 }

 public Guid GetGuid()
 {
 return _guid;
 }
 }

 /// <summary>
 /// 应用程序内固定服务
 /// </summary>
 public class OperationInstance : IOperationInstance
 {
 private readonly Guid _guid;

 public OperationInstance()
 {
 _guid = Guid.NewGuid();
 }

 public OperationInstance(Guid guid)
 {
 _guid = guid == Guid.Empty ? Guid.NewGuid() : guid;
 }

 public Guid GetGuid()
 {
 return _guid;
 }
 }

基础服务具体实现

对基础服务的聚合接口,提供统一服务接口

public interface IOperationService
 {
 /// <summary>
 /// 获取四种形式的Guid码
 /// </summary>
 /// <returns></returns>
 List<string> GetGuidString();
 }

聚合服务接口

对基础服务的聚合实现,将基础服务全部接入进来作为统一服务

/// <summary>
 /// 服务调用
 /// </summary>
 public class OperationService : IOperationService
 {
 public IOperationTransient _transientOperation { get; }
 public IOperationScoped _scopedOperation { get; }
 public IOperationSingleton _singletonOperation { get; }
 public IOperationInstance _instanceOperation { get; }

 public OperationService(IOperationTransient transientOperation,
 IOperationScoped scopedOperation,
 IOperationSingleton singletonOperation,
 IOperationInstance instanceOperation)
 {
 _transientOperation = transientOperation;
 _scopedOperation = scopedOperation;
 _singletonOperation = singletonOperation;
 _instanceOperation = instanceOperation;
 }

 public List<string> GetGuidString()
 {
 return new List<string>()
 {
 $"Transient:"+_transientOperation.GetGuid(),
 $"Scoped:"+_scopedOperation.GetGuid(),
 $"Singleton:" +_singletonOperation.GetGuid(),
 $"Instance:"+_instanceOperation.GetGuid(),
 };
 }
 }

聚合服务的实现

在控制器中进行服务注入

[Route("api/[controller]")]
 [ApiController]
 public class ValuesController : ControllerBase
 {
 private readonly IOperationService _operationService;

 public ValuesController(IOperationService operationService)
 {
 _operationService = operationService;
 }

 [HttpGet]
 [Route(nameof(GetGuidString))]
 public ActionResult<string> GetGuidString()
 {
 return string.Join("\n", _operationService.GetGuidString());
 }
 }

在StartUp中完成服务注入逻辑,这里实现服务注入的方式多种均可。

services.AddTransient<IOperationTransient, OperationTransient>();
services.AddScoped<IOperationScoped, OperationScoped>();
services.AddSingleton<IOperationSingleton, OperationSingleton>();//应用程序启动时便注入该实例
services.AddSingleton<IOperationInstance>(new OperationInstance(Guid.Empty));
services.AddTransient<IOperationService, OperationService>();

通过访问预期Api地址可以得到不同的四种基础服务的Guid信息,

第一次启动程序(不关闭)发起访问:

  

第二次(第一次基础上再次访问)发起访问:

  

可以看见,两次访问下,Singleton和Instance是相同的,都是由应用程序启动时和应用服务加载时决定完毕,Singleton在首次进入服务时进行分配,并始终保持不变,而Instance在应用程序启动时,便将实例注入,进入服务也保持着最先的实例,没有重新分配实例。而Transient和Scoped则进行着变化。

关闭程序,重启,第三次发起访问:

  

可以见到,Singleton和Instance都发生了变化,也说明了之前在Singleton和Instance处写上的作用。

接下来开始设计Transient和Scoped的不同之处,对于已有代码加上新功能,此次我们只针对Scoped和Transient进行比较。

首先在StartUp中将HttpContextAccessor服务注入,目的是在后期能够针对Scoped获取新的服务实例(尽管两个实例是相同的)。

 services.AddHttpContextAccessor();

接着在聚合服务中增加一个方法,用来针对Transient、Scoped测试。

 /// <summary>
 /// 获取Transient、Scoped的Guid码
 /// </summary>
 /// <returns></returns>
 List<string> GetTransientAndScopedGuidString();

在聚合服务实现中实现该方法并对已有的服务重新获取实例,得到不同实例下的Guid码。

public List<string> GetTransientAndScopedGuidString()
 {
 //var tempTransientService = (IOperationTransient)ServiceLocator.Instance.GetService(typeof(IOperationTransient));

 var tempTransientService = (IOperationTransient)_httpContextAccessor.HttpContext.RequestServices.GetService(typeof(IOperationTransient));
 var tempScopedService = (IOperationScoped)_httpContextAccessor.HttpContext.RequestServices.GetService(typeof(IOperationScoped));

 return new List<string>()
 {
 $"原生Transient请求服务:"+_transientOperation.GetGuid(),
 $"手动Transient请求服务:"+ tempTransientService.GetGuid(),
 $"原生Scoped请求服务:"+_scopedOperation.GetGuid(),
 $"手动Scoped请求服务:"+tempScopedService.GetGuid(),
 };
 }

在控制器部分调用该聚合服务即可,并返回相应的结果,本次我返回的结果:

  

可以看到,对于Scoped来讲,一次请求内多次访问同一个服务是共用一个服务实例的,而对于Transient则是,每次访问都是新的服务实例。

至此,对于这四种服务生命周期算是掌握的差不多了。 

参考:

蒋老师文章: https://www.jb51.net/article/150103.htm

田园里的蟋蟀:https://www.jb51.net/article/150102.htm

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对猪先飞的支持。

[!--infotagslink--]

相关文章

  • .NET Core下使用Kafka的方法步骤

    这篇文章主要介绍了.NET Core下使用Kafka的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 通过两种方式增加从库――不停止mysql服务

    一般在线增加从库有两种方式,一种是通过mysqldump备份主库,恢复到从库,mysqldump是逻辑备份,数据量大时,备份速度会很慢,锁表的时间也会很长。另一种是通过xtrabackup工具备份主库,恢复到从库,xtrabackup是物理备份,备份速度快...2015-11-08
  • 详解.NET Core 3.0 里新的JSON API

    这篇文章主要介绍了详解.NET Core 3.0 里新的JSON API,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • Vue生命周期activated之返回上一页不重新请求数据操作

    这篇文章主要介绍了Vue生命周期activated之返回上一页不重新请求数据操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-07-26
  • ASP.NET Core根据环境变量支持多个 appsettings.json配置文件

    这篇文章主要介绍了ASP.NET Core根据环境变量支持多个 appsettings.json配置文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-09-22
  • 记一次EFCore类型转换错误及解决方案

    这篇文章主要介绍了记一次EFCore类型转换错误及解决方案,帮助大家更好的理解和学习使用asp.net core,感兴趣的朋友可以了解下...2021-09-22
  • 安全地关闭MySQL服务的教程

    普通关闭 我的mysql是自己下载的tar包,自己设定安装目录来安装的。停止mysql服务,说来简单,但不知道的话,还真是挠头。在这和mysql入门的同学们共享:)正确方法是,进入mysql的bin目录下,然后执行./mysqladmin -uroot -p shut...2015-11-24
  • 详解ASP.NET Core 中基于工厂的中间件激活的实现方法

    这篇文章主要介绍了ASP.NET Core 中基于工厂的中间件激活的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-09-22
  • vue配置多代理服务接口地址操作

    这篇文章主要介绍了vue配置多代理服务接口地址操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-08
  • 详解.NET Core 使用HttpClient SSL请求出错的解决办法

    这篇文章主要介绍了.NET Core 使用HttpClient SSL请求出错的解决办法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2021-09-22
  • Vue生命周期和钩子函数的详解与经典面试题

    Vue生命周期是指vue实例对象从创建之初到销毁的过程,vue所有功能的实现都是围绕其生命周期进行的,下面这篇文章主要给大家介绍了关于Vue生命周期和钩子函数的相关资料,需要的朋友可以参考下...2021-10-30
  • Underscore源码分析

    Underscore 是一个 JavaScript 工具库,它提供了一整套函数式编程的实用功能,但是没有扩展任何 JavaScript 内置对象。这篇文章主要介绍了underscore源码分析相关知识,感兴趣的朋友一起学习吧...2016-01-02
  • AngularJS内建服务$location及其功能详解

    这篇文章主要为大家详细介绍了AngularJS内建服务$location及$location功能,感兴趣的小伙伴们可以参考一下...2016-07-06
  • React Class组件生命周期及执行顺序

    这篇文章主要介绍了React Class组件生命周期,包括react组件的两种定义方式和class组件不同阶段生命周期函数执行顺序,本文给大家介绍的非常详细,需要的朋友可以参考下...2021-08-14
  • C#创建Windows服务的实现方法

    这篇文章主要介绍了C#创建Windows服务的实现方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C# 启动 SQL Server 服务的实例

    下面小编就为大家分享一篇C# 启动 SQL Server 服务的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-06-25
  • 详解Vue生命周期的示例

    本篇文章主要介绍了详解Vue生命周期的示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧 ...2017-03-13
  • 浅谈vue生命周期共有几个阶段?分别是什么?

    这篇文章主要介绍了浅谈vue生命周期共有几个阶段?分别是什么?具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-07
  • c#编写webservice服务引用实例分享

    c#编写webservice服务引用实例分享,大家参考使用吧...2020-06-25
  • C#使用windows服务开启应用程序的方法

    这篇文章主要介绍了C#使用windows服务开启应用程序的方法,实例分析了C#操作windows服务开启应用程序所遇到的问题及相关解决技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25