欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-11-14 11:48:52
前言 最近在做一个小的demo中,在一个界面上两次调用视图组件,并且在视图组件中都调用了数据库查询,结果发现,一直报错,将两个视图组件的调用分离,单独进行,却又是正常的,...

前言

最近在做一个小的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信息,

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

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

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

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

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

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

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

可以见到,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(),
 };
 }

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

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

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

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

参考:

蒋老师文章:

田园里的蟋蟀:

总结

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