Asp.NetCore中服务的⽣命周期选项区别与⽤法详解
前⾔
最近在做⼀个⼩的Demo中,在⼀个界⾯上两次调⽤视图组件,并且在视图组件中都调⽤了数据库查询,结果发现,⼀直报错,将两个视图组件的调⽤分离,单独进⾏,却⼜是正常的,寻⼀番,发现是配置依赖注⼊服务时,对于服务的⽣命周期没有配置得当导致,特此做⼀次实验来认识三者之间(甚⾄是四者之间的⽤法及区别)。
⼀、服务的⽣命周期
在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;
}
}
typeof的用法/// <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则是,每次访问都是新的服务实例。
⾄此,对于这四种服务⽣命周期算是掌握的差不多了。
参考:
总结
以上就是这篇⽂章的全部内容了,希望本⽂的内容对⼤家的学习或者⼯作具有⼀定的参考学习价值,如果有疑问⼤家可以留⾔交流,谢谢⼤家对的⽀持。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。