asp.net core 系列 4 注入服务的生存期

一.服务的生存期

在容器中每个注册的服务,根据程序应用需求都可以选择合适的服务生存期,ASP.NET Core 服务有三种生存期配置:

  • Transient:暂时生存期,在每次请求时被创建。 这种生存期适合轻量级的,无状态的服务。
  • Scoped: 作用域生存期,在每次请求被创建一次。
  • 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
        }</pre>

(3)单例注册的服务,实例对象对每个对象和每个请求都是相同的。相当于在整个应用Application中只实例化一次,常见的单例模式。

参考文献:

官方文档:ASP.NET Core

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容