Asp.Net Core中服务的生命周期选项区别和用法
在做一个小的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>();
二、代码设计服务生命周期
首先设计一些服务相关的操作接口
1 public interface ioperation 2 { 3 guid getguid(); 4 } 5 6 public interface ioperationtransient: ioperation 7 { 8 9 } 10 11 public interface ioperationscoped : ioperation 12 { 13 14 } 15 16 public interface ioperationsingleton : ioperation 17 { 18 19 } 20 21 public interface ioperationinstance : ioperation 22 { 23 24 }
其次对这些操作类予以实现并生成相关服务
1 /// <summary> 2 /// 常规服务 3 /// </summary> 4 public class operation : ioperation 5 { 6 private readonly guid _guid; 7 8 public operation() 9 { 10 _guid = guid.newguid(); 11 } 12 13 public operation(guid guid) 14 { 15 _guid = guid == guid.empty ? guid.newguid() : guid; 16 } 17 18 public guid getguid() 19 { 20 return _guid; 21 } 22 } 23 24 /// <summary> 25 /// 瞬时服务 26 /// </summary> 27 public class operationtransient : ioperationtransient 28 { 29 private readonly guid _guid; 30 31 public operationtransient() 32 { 33 _guid = guid.newguid(); 34 } 35 36 public operationtransient(guid guid) 37 { 38 _guid = guid == guid.empty ? guid.newguid() : guid; 39 } 40 41 public guid getguid() 42 { 43 return _guid; 44 } 45 } 46 47 /// <summary> 48 /// 单次请求内服务固定 49 /// </summary> 50 public class operationscoped : ioperationscoped 51 { 52 private readonly guid _guid; 53 54 public operationscoped() 55 { 56 _guid = guid.newguid(); 57 } 58 59 public operationscoped(guid guid) 60 { 61 _guid = guid == guid.empty ? guid.newguid() : guid; 62 } 63 64 public guid getguid() 65 { 66 return _guid; 67 } 68 } 69 70 71 /// <summary> 72 /// 所有请求内固定服务 73 /// </summary> 74 public class operationsingleton : ioperationsingleton 75 { 76 private readonly guid _guid; 77 78 public operationsingleton() 79 { 80 _guid = guid.newguid(); 81 } 82 83 public operationsingleton(guid guid) 84 { 85 _guid = guid == guid.empty ? guid.newguid() : guid; 86 } 87 88 public guid getguid() 89 { 90 return _guid; 91 } 92 } 93 94 /// <summary> 95 /// 应用程序内固定服务 96 /// </summary> 97 public class operationinstance : ioperationinstance 98 { 99 private readonly guid _guid; 100 101 public operationinstance() 102 { 103 _guid = guid.newguid(); 104 } 105 106 public operationinstance(guid guid) 107 { 108 _guid = guid == guid.empty ? guid.newguid() : guid; 109 } 110 111 public guid getguid() 112 { 113 return _guid; 114 } 115 }
对基础服务的聚合接口,提供统一服务接口
public interface ioperationservice { /// <summary> /// 获取四种形式的guid码 /// </summary> /// <returns></returns> list<string> getguidstring(); }
对基础服务的聚合实现,将基础服务全部接入进来作为统一服务
1 /// <summary> 2 /// 服务调用 3 /// </summary> 4 public class operationservice : ioperationservice 5 { 6 public ioperationtransient _transientoperation { get; } 7 public ioperationscoped _scopedoperation { get; } 8 public ioperationsingleton _singletonoperation { get; } 9 public ioperationinstance _instanceoperation { get; } 10 11 public operationservice(ioperationtransient transientoperation, 12 ioperationscoped scopedoperation, 13 ioperationsingleton singletonoperation, 14 ioperationinstance instanceoperation) 15 { 16 _transientoperation = transientoperation; 17 _scopedoperation = scopedoperation; 18 _singletonoperation = singletonoperation; 19 _instanceoperation = instanceoperation; 20 } 21 22 public list<string> getguidstring() 23 { 24 return new list<string>() 25 { 26 $"transient:"+_transientoperation.getguid(), 27 $"scoped:"+_scopedoperation.getguid(), 28 $"singleton:" +_singletonoperation.getguid(), 29 $"instance:"+_instanceoperation.getguid(), 30 }; 31 } 32 }
在控制器中进行服务注入
1 [route("api/[controller]")] 2 [apicontroller] 3 public class valuescontroller : controllerbase 4 { 5 private readonly ioperationservice _operationservice; 6 7 public valuescontroller(ioperationservice operationservice) 8 { 9 _operationservice = operationservice; 10 } 11 12 [httpget] 13 [route(nameof(getguidstring))] 14 public actionresult<string> getguidstring() 15 { 16 return string.join("\n", _operationservice.getguidstring()); 17 } 18 }
在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测试。
1 /// <summary> 2 /// 获取transient、scoped的guid码 3 /// </summary> 4 /// <returns></returns> 5 list<string> gettransientandscopedguidstring();
在聚合服务实现中实现该方法并对已有的服务重新获取实例,得到不同实例下的guid码。
1 public list<string> gettransientandscopedguidstring() 2 { 3 //var temptransientservice = (ioperationtransient)servicelocator.instance.getservice(typeof(ioperationtransient)); 4 5 var temptransientservice = (ioperationtransient)_httpcontextaccessor.httpcontext.requestservices.getservice(typeof(ioperationtransient)); 6 var tempscopedservice = (ioperationscoped)_httpcontextaccessor.httpcontext.requestservices.getservice(typeof(ioperationscoped)); 7 8 return new list<string>() 9 { 10 $"原生transient请求服务:"+_transientoperation.getguid(), 11 $"手动transient请求服务:"+ temptransientservice.getguid(), 12 $"原生scoped请求服务:"+_scopedoperation.getguid(), 13 $"手动scoped请求服务:"+tempscopedservice.getguid(), 14 }; 15 }
在控制器部分调用该聚合服务即可,并返回相应的结果,本次我返回的结果:
可以看到,对于scoped来讲,一次请求内多次访问同一个服务是共用一个服务实例的,而对于transient则是,每次访问都是新的服务实例。
至此,对于这四种服务生命周期算是掌握的差不多了。
参考:
蒋老师文章:
田园里的蟋蟀:
2018-10-20,望技术有成后能回来看见自己的脚步
上一篇: 小米、闻泰等成为高通关键伙伴 将首批发布骁龙X50基带5G手机
下一篇: 推送算法小结