一. 服务的生存期
在容器中每个注册的服务, 根据程序应用需求都可以选择合适的服务生存期, ASP.NET Core 服务有三种生存期配置:
(1) Transient: 暂时生存期, 在每次请求时被创建. 这种生存期适合轻量级的, 无状态的服务.
(2) Scoped: 作用域生存期, 在每次请求被创建一次.
(3) Singleton: 单例生存期, 在它们第一次被请求时创建. 每个后续请求将使用相同的实例. 如果应用程序需要单例行为, 建议让服务容器管理服务的生命周期, 而不是在自己的类中实现单例模式.
1.1 演示案例
为了演示生存期和注册选项之间的差异, 以下服务接口, 任务是演示标识符 OperationId 的操作值变化. 根据为以下接口配置操作服务的生存期的方式, 容器在类请求时提供相同或不同的服务实例:
- public interface IOperation
- {
- Guid OperationId { get; }
- }
- // 用于演示暂时生存期
- public interface IOperationTransient : IOperation
- {
- }
- // 用于演示作用域生存期
- public interface IOperationScoped : IOperation
- {
- }
- // 用于演示单例生存期
- public interface IOperationSingleton : IOperation
- {
- }
- // 用于演示单例中空 GUID
- public interface IOperationSingletonInstance : IOperation
- {
- }
上面四种服务接口在 Operation 类中实现. 调用 Operation 类时将自动生成一个 GUID(如果实例化 Operation 类时没有指定 GUID), 下面是 Operation 类的实现:
- public class Operation : IOperationTransient,
- IOperationScoped,
- IOperationSingleton,
- IOperationSingletonInstance
- {
- /// <summary>
- /// 构造方法中生成 GUID, 在实例化类时
- /// </summary>
- public Operation() : this(Guid.NewGuid())
- {
- }
- public Operation(Guid id)
- {
- OperationId = id;
- }
- /// <summary>
- /// 获取 GUID
- /// </summary>
- public Guid OperationId { get; private set; }
- }
再注册一个 OperationService 服务, 该服务取决于每个其他 Operation 类型. 当通过依赖关系注入请求 OperationService 时, 它将接收每个服务的新实例或基于从属服务 (Operation ) 的生存期的现有实例. OperationService 服务作用就是第二次调用 Operation 类, 查看 Operation 类实例的作用域变化.
- public class OperationService
- {
- public IOperationTransient TransientOperation { get; }
- public IOperationScoped ScopedOperation { get; }
- public IOperationSingleton SingletonOperation { get; }
- public IOperationSingletonInstance SingletonInstanceOperation { get; }
- public OperationService(
- IOperationTransient transientOperation,
- IOperationScoped scopedOperation,
- IOperationSingleton singletonOperation,
- IOperationSingletonInstance instanceOperation)
- {
- TransientOperation = transientOperation;
- ScopedOperation = scopedOperation;
- SingletonOperation = singletonOperation;
- SingletonInstanceOperation = instanceOperation;
- }
- }
(1) 如果在请求时创建了临时服务(Transient), 则 IOperationTransient 服务的 OperationId 与 OperationService 的 OperationId 不同. OperationService 将接收 IOperationTransient 类的新实例. 新实例将生成一个不同的 OperationId.
(2) 如果按请求创建有作用域的服务(Scoped), 则 IOperationScoped 服务的 OperationId 与请求中 OperationService 的该 ID 相同. 在请求中, 两个服务共享不同的 OperationId 值.
(3) 如果单一实例服务(Singleton), 则只创建一次 并在所有请求和所有服务中使用, 则 OperationId 在所有服务请求中保持不变.
下面是在 Startup.ConfigureServices 服务容器中注册, 指定服务的生存期:
- services.AddTransient<IOperationTransient, Operation>();
- services.AddScoped<IOperationScoped, Operation>();
- services.AddSingleton<IOperationSingleton, Operation>();
- services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
- services.AddTransient<OperationService, OperationService>();
为了演示各个请求中的对象生存期. 下面示例应用 Index 页面, 请求 IOperation 类型和 OperationService. 然后查看 Operation 类属性 OperationId 值的变化:
- public class IndexModel : PageModel
- {
- public OperationService OperationService { get; }
- public IOperationTransient TransientOperation { get; }
- public IOperationScoped ScopedOperation { get; }
- public IOperationSingleton SingletonOperation { get; }
- public IOperationSingletonInstance SingletonInstanceOperation { get; }
- public IndexModel(
- OperationService operationService,
- IOperationTransient transientOperation,
- IOperationScoped scopedOperation,
- IOperationSingleton singletonOperation,
- IOperationSingletonInstance singletonInstanceOperation)
- {
- OperationService = operationService;
- TransientOperation = transientOperation;
- ScopedOperation = scopedOperation;
- SingletonOperation = singletonOperation;
- SingletonInstanceOperation = singletonInstanceOperation;
- }
- public string BindGUIDMsg { get; set; }
- public void OnGet()
- {
- BindGUIDMsg += "IOperation 操作: <br/>";
- BindGUIDMsg += "暂时性:" + TransientOperation.OperationId.ToString() + "</br>";
- BindGUIDMsg += "有作用域:" + ScopedOperation.OperationId.ToString() + "</br>";
- BindGUIDMsg += "单一实例:" + SingletonOperation.OperationId.ToString() + "</br>";
- BindGUIDMsg += "实例:" + SingletonInstanceOperation.OperationId.ToString() + "</br>";
- BindGUIDMsg += "</br></br></br>OperationService 操作:</br>";
- BindGUIDMsg += "暂时性:" + OperationService.TransientOperation.OperationId.ToString() + "</br>";
- BindGUIDMsg += "有作用域:" + OperationService.ScopedOperation.OperationId.ToString() + "</br>";
- BindGUIDMsg += "单一实例:" + OperationService.SingletonOperation.OperationId.ToString() + "</br>";
- BindGUIDMsg += "实例:" + OperationService.SingletonInstanceOperation.OperationId.ToString() + "</br>";
- }
- }
- <div>
- @{
- @html.Raw(@Model.BindGUIDMsg);
- }
- </div>
第一次 Index 页面请求:
IOperation 操作:
暂时性: 8ef874a3-743d-4288-98d4-3df126cd940d
有作用域: 256ff050-f469-4ea3-8dde-16cdd3087c83
单一实例: d2caf297-a9b1-4dcf-ADDA-c68e46fe0741
实例: 00000000-0000-0000-0000 -000000000000
OperationService 操作:
暂时性: 5411fd0d-f2e1-4885-beee-2d7ccf48dceb
有作用域: 256ff050-f469-4ea3-8dde-16cdd3087c83
单一实例: d2caf297-a9b1-4dcf-adda-c68e46fe0741
实例: 00000000-0000-0000- 0000-000000000000
第二次 Index 页面请求:
IOperation 操作:
暂时性: e685fd0e-d2e0-4900-9eff-e6bc41cd2f80
有作用域: ca233b49-8326-4a7e-8ee4-6993d70786ed
单一实例: d2caf297-a9b1-4dcf-adda-c68e46fe0741
实例: 00000000-0000-0000-0000-000000000000
OperationService 操作:
暂时性: db89be00-c3b7-4f99-bead-5be693ccc2c0
有作用域: ca233b49-8326-4a7e-8ee4-6993d70786ed
单一实例: d2caf297-a9b1-4dcf-adda-c68e46fe0741
实例: 00000000-0000-0000-0000-000000000000
下面再总结一下:
(1)暂时性注册的服务, 每次调用服务都会是一个新的服务对象实例. 相当于在 IndexModel 类的局部 (方法或属性中) 实例化一个依赖对象 Operation 类, 伪代码是:
- public class IndexModel
- {
- public void OnGet()
- {
- // 加载 index 页时, 实例化了二次 Operation 类
- // 第一次
- OperationService operationService=new OperationService();
- // 第二次
- IOperationTransient TransientOperation =new Operation();
- }
- }
(2)作用域注册的服务, 一次请求内 (加载一次 index 页) 对象实例是相同的, 但每次请求会产生一个新实例. 相当于在 IndexModel 类的全局中实例化一次依赖对象 Operation 类, 伪代码是:
- OperationService operationService = null;
- public IndexModel()
- {
- operationService = new OperationService();
- operationService.ScopedOperation = new Operation();
- }
- public void OnGet()
- {
- operationService.ScopedOperation.OperationId;
- IOperationScoped operationScoped=operationService.ScopedOperation;
- operationScoped.OperationId
- }
(3)单例注册的服务, 实例对象对每个对象和每个请求都是相同的. 相当于在整个应用 Application 中只实例化一次, 常见的单例模式.
参考文献:
官方文档: ASP.NET Core
来源: https://www.cnblogs.com/MrHSR/p/10234436.html