时间:2021-05-28
前言
最近在做一个小的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
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
声明:本页内容来源网络,仅供用户参考;我单位不保证亦不表示资料全面及准确无误,也不保证亦不表示这些资料为最新信息,如因任何原因,本网内容或者用户因倚赖本网内容造成任何损失或损害,我单位将不会负任何法律责任。如涉及版权问题,请提交至online#300.cn邮箱联系删除。
前言本文主要给大家介绍的是关于Asp.Net中WebForm生命周期的相关内容,分享出来供大家参考学习,下面来看看详细的介绍:一.Asp.Net页面生命周期的概
Asp.net是微软.Net战略的一个组成部分。它相对以前的Asp有了很大的发展,引入了许多的新机制。本文就Asp.net页面的生命周期向大家做一个初步的介绍,
本文实例讲述了React生命周期原理与用法。分享给大家供大家参考,具体如下:React生命周期生命周期概览生命周期的状态组件的生命周期可分成三个状态:Mount
如果我们能更好地掌握这样一个过程,那么对单个ASP.NETPage的生命周期也能更好地了解:下面介绍如何编写一个简单的ASP.NET页面和一个简单的HttpMo
jsp生命周期理解JSP底层功能的关键就是去理解它们所遵守的生命周期。JSP生命周期就是从创建到销毁的整个过程,类似于servlet生命周期,区别在于JSP生命