首页 > 其他分享 >【3】.NET6最通俗易懂的依赖注入之服务容器与作用域

【3】.NET6最通俗易懂的依赖注入之服务容器与作用域

时间:2023-02-10 16:44:52浏览次数:56  
标签:容器 服务 作用域 创建 通俗易懂 GetService 实例 NET6

这篇文章是 ASP.NET 6 依赖注入系列文章的第 3 篇。

在上一篇文章中,我们讨论依赖注入的基本用法与生命周期

接下来,在这篇文章中,我们继续深入了解服务容器相关的概念。

服务容器

我们回顾一下什么是服务容器。

在上一篇文章中,我们提到过,依赖注入系统中的服务容器会保存,由依赖注入系统创建的具有有效生命周期的服务实例。

「在 ASP.NET 的依赖注入系统中,服务容器有根容器和子容器之分。」

第一个在依赖注入系统中创建出来的容器就是根容器,而且只会有一个根容器存在,子容器可以有很多个。

它们的区别在于:

  •  「根容器中保存的实例对于所有子容器都是可见的。」
  •  「子容器中保存的的实例互相不可见。」
  •  「不同生命周期模式的服务实例,会被保存在不同的容器里。」

单例模式的服务实例被保存在根容器中。

作用域模式的服务实例被保存在作用域的容器中,作用域的容器就是子容器。

由于子容器互相不可见,所以作用域模式中保存的实例,只会在本作用域中生存。

瞬时模式的服务实例不会被保存在任何容器中的。

子容器示例

我们来看这个示例,演示了三种不同生命周期模式的区别:

public interface IAccount{ }
public interface IMessage{ }
public interface ITool{ }

public class Base
{
    public Base()
    {
        Console.WriteLine($"Created:{GetType().Name}");
    }

}

public class Account: Base, IAccount {}
public class Message:Base, IMessage {}
public class Tool:Base, ITool {}

public static void Main()
{
    // 创建服务集合
    var serviceCollection = new ServiceCollection()
        .AddTransient<IAccount, Account>()
        .AddScoped<IMessage, Message>()
        .AddSingleton<ITool, Tool>();
    
    // 创建 ServiceProvider 对象代表的服务根容器
    var root = serviceCollection.BuildServiceProvider();
    // 创建子容器1
    var child1 = root.CreateScope().ServiceProvider;
    // 创建子容器2
    var child2 = root.CreateScope().ServiceProvider;
 
    // 获取子容器1的服务实例
    GetService<IAccount>(child1);
    GetService<IMessage>(child1);
    GetService<ITool>(child1);
   
    Console.WriteLine();
    
    // 获取子容器2的服务实例
    GetService<IAccount>(child2);
    GetService<IMessage>(child2);
    GetService<ITool>(child2);
}

// 为验证生命周期,连续获取两次
public static void GetService<T>(IServiceProvider provider)
{
    provider.GetService<T>();
    provider.GetService<T>();
}

 

创建容器的代码与上一篇文章中的示例相同,这里我们主要关注两个 CreateScope 方法的使用:

var root = serviceCollection.BuildServiceProvider();
var child1 = root.CreateScope().ServiceProvider;
var child2 = root.CreateScope().ServiceProvider;

 

通过使用根容器的 CreateScope 方法,可以创建出 ServiceScope 对象,它就代表了服务的作用域。

每个作用域中,都有一个 ServiceProvider 对象,它代表服务容器。

我们可以通过服务根容器,去创建它的子容器。

最后,我们通过两个子容器去获取相应的服务实例:

// 获取子容器1的服务实例
GetService<IAccount>(child1);
GetService<IMessage>(child1);
GetService<ITool>(child1);

Console.WriteLine();

// 获取子容器2的服务实例
GetService<IAccount>(child2);
GetService<IMessage>(child2);
GetService<ITool>(child2);

 

Base 类中的构造函数,会在控制台中打印被创建的具体实例,因此这个示例的运行结果如下:

.NET6最通俗易懂的依赖注入之服务容器与作用域

通过结果可以发现:

Account 服务的生命周期为瞬时,所以两次获取该服务的实例时,都会创建一个全新的 Account 对象。

Message 服务的生命周期是作用域,如果在同一个子容器中去获取它的服务实例,那么只会创建一个 Message 对象,而这里有两个子容器,所以创建了两个 Message 对象。

Tool 服务的生命周期是单例,单例模式的服务实例,只保存在根容器上,并且对所有子容器可见。由于两个子容器拥有同样的根容器,所以只有第一次获取时 Tool 对象才会被创建。

服务作用域

每一个新创建的服务作用域ServiceScope 对象都具有一个ServiceProvider对象,它代表的是服务容器。

「根容器可以创建子容器,子容器也可以创建子容器。」

但所有的子容器都是平级的,也就是说无论它是由谁创建的,它都是根容器的子容器,而不存在所谓的“孙子容器”。

虽然根容器与子容器是父子关系,但其实「子容器并不知道自己的父容器是谁,它们只知道根容器是谁」。

在子容器的眼中,只有君没有父。

对于 ASP.NET 应用来说,服务作用域具有非常明确的边界,也就是每个 HTTP 的请求上下文。

也就是说,服务作用域的生命周期与每个请求上下文是绑定在一起的。

我们现在知道,容器有根容器和子容器之分。

其实根容器和子容器也有不同的身份,我们来看这张图:

.NET6最通俗易懂的依赖注入之服务容器与作用域

「顶端的是根容器,也称应用容器。」

「分支都是子容器,它们都是根据请求创建和释放的子容器,也称请求容器。」

在 ASP.NET 应用初始化过程中,会用到大量的内置服务实例,而这些服务实例都是由应用容器提供。

在具体处理某个请求的时候,ASP.NET 框架会针对当前请求,创建一个服务作用域对象。

这个服务作用域对象中的请求容器,用来提供当前请求处理过程中所需的服务实例。

 

原文地址:https://www.dongchuanmin.com/net/2010.html

标签:容器,服务,作用域,创建,通俗易懂,GetService,实例,NET6
From: https://www.cnblogs.com/Echoxxx/p/17109499.html

相关文章

  • Go 语言变量作用域
    作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。Go语言中变量可以在三个地方声明:函数内定义的变量称为局部变量函数外定义的变量称为......
  • js基础笔记学习88-作用域链
    ......
  • js基础笔记学习87-函数作用域
    ......
  • C语言--变量的作用域与生命期
    局部变量--函数内部定义的变量(隶属于当前函数)--只能在当前函数中访问全局变量--全局范围内的变量(不特定隶属于任意一个函数)--可以在任意函数中访问同......
  • js函数作用域和作用域链
    定义:作用域就是限制某个变量只能在某个区域内有效。全局变量拥有全局作用域,而局部变量拥有局部作用域。在js中,作用域一共分为三类:全局作用域、局部(函数)作用域、块级作用域......
  • .Net6对AOP的多种支持之IAsyncResourceFilter
     环境:.Net6Web项目Mvc结构开发工具:VS2022IAsyncResourceFilter(资源缓存异步)IAsyncResourceFilter扩展   ASP.NETCore6提供的是接口IAsyncResourceFilter......
  • ES6块级作用域let声明和const声明以及与var之间的区别
    一、ES6块级作用域let声明块级声明用于声明在指定作用域之外无法访问的变量,存在于:①函数内部②块内(字符{和}之间的区域)禁止重声明(1)如果在作用域由已经存在某个标......
  • Java变量、常量、作用域、运算符
    一、变量可以变化的量Java是强类型语言,每一个变量必须声明其类型Java变量是程序中最基本的存储单元,其要素包括变量名、变量类型和作用域typevarName[=value][{,var......
  • js中不存在块级作用域 js 230208
    ......
  • 通俗易懂的理解java泛型
    泛型是JAVA1.5版本之后引入的一个新特性,它允许在定义类、接口和方法的时候使用类型参数,这些类型参数将在使用时确定。泛型的引入,使得JAVA具有了更好的代码复用性,更好的类......