zoukankan      html  css  js  c++  java
  • 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>();

    二、代码设计服务生命周期

      首先设计一些服务相关的操作接口

     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("
    ", _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则是,每次访问都是新的服务实例。

      至此,对于这四种服务生命周期算是掌握的差不多了。

      参考:

         蒋老师文章: 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,望技术有成后能回来看见自己的脚步
  • 相关阅读:
    Visual C# 插件构架实战
    用CSS实现表格单元格数据自动换行或不换行
    页面代码开发规范:CSS命名规范(参考)
    UML之业务建模
    在.NET环境下将报表导出Excel和Word(网络转载)
    asp.net数据导出到Excel (从网络摘操)
    ASP.NET(VB)把数据导出到EXCEL的一种方法(网上转载)
    关于ASP,ASP.NET,VB.NET里的MD5加密函数(转载)
    .net打包自动安装数据库(转载)
    Cannot load required extension: curl
  • 原文地址:https://www.cnblogs.com/CKExp/p/9823076.html
Copyright © 2011-2022 走看看