Asp.Net Core中服务的生命周期选项区别和用法
在做一个小的Demo中,在一个界面上两次调用视图组件,并且在视图组件中都调用了数据库查询,结果发现,一直报错,将两个视图组件的调用分离,单独进行,却又是正常的,寻找一番,发现是配置依赖注入服务时,对于服务的生命周期没有配置得当导致,特此做一次实验来认识三者之间(甚至是四者之间的用法及区别)。
本文demo地址(具体见WebApi控制器中):https://gitee.com/530521314/Partner.TreasureChest/tree/master/ServiceLifetime
一、服务的生命周期
在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则是,每次访问都是新的服务实例。
至此,对于这四种服务生命周期算是掌握的差不多了。
参考:
蒋老师文章: http://www.cnblogs.com/artech/p/asp-net-core-di-register.html
田园里的蟋蟀:https://www.cnblogs.com/xishuai/p/asp-net-core-ioc-di-get-service.html
2018-10-20,望技术有成后能回来看见自己的脚步
Asp.Net Core中服务的生命周期选项区别和用法的更多相关文章
- 2、ASP.NET Core中服务的生命周期
ASP.NET Core支持依赖注入软件设计模式,它允许在不同的组件中注入我们的服务,并且控制服务的初始化.有些服务可以在短时间内初始化,并且只能在某个特别的组件,以及请求中才能用到:而还有一些服务, ...
- Android中服务的生命周期与两种方式的区别
服务的生命周期跟Activity的生命周期类似.但是生命周期甚至比你关注服务如何创建和销毁更重要,因为服务能够在用户不知情的情况下在后台运行. 服务的生命周期---从创建到销毁---可以被分为以下两个 ...
- Android中服务的生命周期回调方法
- Asp.net MVC中Html.Partial, RenderPartial, Action,RenderAction 区别和用法(mvc部分视图的添加)
Partial 和RenderPartial:这两个的性质都是一样, 只指把一个个View给镶入进来, 只是回传值有点不一样Partial 回传的一个Object (MvcHtmlString), 回 ...
- Asp.net MVC中Html.Partial, RenderPartial, Action,RenderAction 区别和用法
Partial 和RenderPartial:这两个的性质都是一样, 只指把一个个View给镶入进来, 只是回传值有点不一样Partial 回传的一个Object (MvcHtmlString), 回 ...
- Asp.net MVC中Html.Partial, RenderPartial, Action,RenderAction 区别和用法【转发】
Html.partial和RenderPartial的用法与区别Html.partial和RenderPartial都是输出html片段,区别在于Partial是将视图内容直接生成一个字符串并返回(相 ...
- [转]Asp.net MVC中Html.Partial, RenderPartial, Action,RenderAction 区别和用法
本文转自:http://www.cnblogs.com/gesenkof99/archive/2013/06/03/3115052.html Partial 和RenderPartial:这两个的性质 ...
- gRPC在 ASP.NET Core 中应用学习(二)
前言: 上一篇文章中简单的对gRPC进行了简单了解,并实现了gRPC在ASP.NET Core中服务实现.客户端调用:那么本篇继续对gRPC的4中服务方法定义.其他使用注意点进一步了解学习 一.gRP ...
- ASP.NET Core中的依赖注入(4): 构造函数的选择与服务生命周期管理
ServiceProvider最终提供的服务实例都是根据对应的ServiceDescriptor创建的,对于一个具体的ServiceDescriptor对象来说,如果它的ImplementationI ...
随机推荐
- JAVA WEB项目中生成验证码及验证实例(附源码及目录结构)
[我是一个初学者,自己总结和网上搜索资料,代码是自己敲了一遍,亲测有效,现将所有的目录结构和代码贴出来分享给像我一样的初学者] 作用 验证码为全自动区分计算机和人类的图灵测试的缩写,是一种区分用户是计 ...
- netty源码分析之揭开reactor线程的面纱(一)
netty最核心的就是reactor线程,对应项目中使用广泛的NioEventLoop,那么NioEventLoop里面到底在干些什么事?netty是如何保证事件循环的高效轮询和任务的及时执行?又是如 ...
- 解锁 vmware esxi 6.7 并安装 mac os 10.13
1.安装 esxi 6.7 2.下载 unlocker 2.1.1.zip 3.上传 unlocker 2.1.1.zip esxi的磁盘中 4.开启esxi的ssh登录 5.使用 ssh 登录 es ...
- BZOJ_3316_JC loves Mkk_ 二分答案 + 单调队列
BZOJ_3316_JC loves Mkk_ 二分答案 + 单调队列 题意: 分析: 拆成链,二分答案,奇偶两个单调队列维护最大子段和,记录方案. 代码: #include <cstdio&g ...
- angular开发环境配置全套教程
第一步.安装node.js 1.需要下载的文件路径:https://nodejs.org/en/download/ 2.安装nodejs: 3.验证node.js 4.npm安装以及验证: npm的安 ...
- QTimer在QThread环境中失效的问题
QTimer在非QThread的环境下能正常工作.但在QThread环境下,需要做一些改动才能正常工作. 创建Qt的线程有两种方式: 1. 子例化QThread 可以在虚函数run中启动定时器,大致的 ...
- 带着新人看java虚拟机07(多线程篇)
这一篇说一下比较枯燥的东西,为什么说枯燥呢,因为我写这都感觉很无聊,无非就是几个阻塞线程的方法和唤醒线程的方法... 1.线程中断 首先我们说一说怎么使得一个正在运行中的线程进入阻塞状态,这也叫做线程 ...
- Node中流的概念
在学习node的过程中,对于流的概念一直不是很理解,通过查阅一些资料,现在将自己对流的一些理解进行总结一下. 一.流的理解 首先我们必须知道什么是流,很多书中只是提到使用流读写文件怎么怎么方便,却不提 ...
- css属性分类介绍
css属性分类介绍 CSS分类目录 文本/字体/颜色 文本相关 字体相关 颜色相关 背景相关 大小/布局 大小属性 margin 外边距 padding 内边距 border 边框 position ...
- 浅谈java线程池实现
再进入主题之前,我们先了解几个概念,对读源码有所帮助,对于线程池的运行状态,有4个级别,分别是RUNNING,SHUTING,STOP,TIDING,TERMINATED 解释如下: The runS ...