zoukankan      html  css  js  c++  java
  • ASP.NET Core 中的依赖注入

    什么是依赖注入

    软件设计原则中有一个依赖倒置原则(DIP),为了更好的解耦,讲究要依赖于抽象,不要依赖于具体。而控制反转(Ioc)就是这样的原则的其中一个实现思路, 这个思路的其中一种实现方式就是依赖注入(DI)。

    • 什么是依赖:当一个类需要另一个类协作来完成工作的时候就产生了依赖。

    • 什么是注入: 注入体现的是一个IOC(控制反转的的思想)。正转是自己来实例化需要的依赖。反转是类不应该自己创建它,而是应该由它的调用者传给它。于是可以通过构造函数等让外界把依赖传给类。

    • 为什么要反转 为了在业务变化的时候尽少改动代码可能造成的问题。基于抽象添加新的实现。只需要在原来注入的地方改一下就可以了。

    • 什么是容器 容器统一管理系统中的所有依赖。容器负责两件事情:

      • 绑定服务与实例之间的映射关系
      • 获取实例并对实例进行管理(创建与销毁)

      容器

    ASP .NET Core 中使用依赖注入

    ASP.NET Core 中的依赖注入

    • IServiceCollection 负责注册服务,是一个IList类型的集合。
    • IServiceProvider 负责提供实例,是由IServiceCollection的扩展方法BuildServiceProvider创建的。
    • ServiceDescriptor 单个服务描述
      • Type ServiceType: 服务的类型
      • Type ImplementationType: 实现的类型
      • ServiceLifetime Lifetime: 服务的生命周期
      • object ImplementationInstance: 实现服务的实例
      • Func<IServiceProvider, object> ImplementationFactory: 创建服务实例的工厂

    注册

    ServiceCollection提供了三种注册方法分别对应着三种实例生命周期。

    • AddSingleton 整个应用程序生命周期以内只创建一个实例
    • AddScoped 在同一个Scope内只初始化一个实例,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)
    • AddTransient 每一次GetService都会创建一个新的实例

    做一个简单测试:

    1. 创建测试类:

      public interface ITest
      {
          Guid Guid { get; }
      }
      
      public class Test : ITest
      {
          public Guid Guid { get; }
      
          public Test()
          {
              Guid = Guid.NewGuid();
          }
      }
      
    2. 在ConfigureServices里注册

      public void ConfigureServices(IServiceCollection services)
      {
          services.AddTransient<ITest, Test>();
      }
      
    3. 通过三种方法来获取这个Test类实例, Controller和View中代码如下

      public class HomeController : Controller
      {
          private ITest _test;
          private ILogger<HomeController> _logger;
      
          public HomeController(ITest test, ILogger<HomeController> logger)
          {
              this._test = test;
              this._logger = logger;
          }
      
          public IActionResult Index()
          {
              //通过构造函数获取
              var res1 = this._test;
              ViewBag.TestFromConstructor = res1;
      
              //通过HttpContext获取
              var res2 = HttpContext.RequestServices.GetService<ITest>();
              ViewBag.TestFromContext = res2;
      
              return View();
          }
      }
      
      @inject ITest TestFromView
      <ul>
          <li>@ViewBag.TestFromConstructor.Guid</li>
          <li>@ViewBag.TestFromContext.Guid</li>
          <li>@TestFromView.Guid</li>
      </ul>
      
    4. 运行,结果如下

      03d437d6-2f18-452e-a7fd-ce62cea90381
      08b31487-b02b-4d62-bc2b-6d2026389f0c
      21a7fc13-6e7b-4590-910b-68d21a7a03d1
      

      说明三种方式获取了三个不同的实例, 刷新一下页面, 又变成了另外三个不同的值.

    5. 现在在startup文件中将原来的 services.AddTransient<ITest,Test>() 改为 services.AddScoped<ITest,Test>() , 其他不变, 重新运行一下, 结果如下

      050fef7e-2dc3-4d7d-8733-683b54b40b0b
      050fef7e-2dc3-4d7d-8733-683b54b40b0b
      050fef7e-2dc3-4d7d-8733-683b54b40b0b
      

      刷新一下:

      c9e5df8d-b085-4e3a-b883-fa083ba1d136
      c9e5df8d-b085-4e3a-b883-fa083ba1d136
      c9e5df8d-b085-4e3a-b883-fa083ba1d136
      

      三组数字相同, 刷新一下, 又变成了另外三组一样的值, 这说明在同一次请求里, 获取的实例是同一个。

      最常用的DBContext默认构建为Scope实例。即能减少实例初始化的消耗,还能实现跨Service事务的功能。

    6. 再将 services.AddScoped<ITest,Test>() 改为 services.AddSingleton<ITest,Test>() , 重新运行, 这次结果是

      42ef5162-5781-427b-ac9d-a152500ed32f
      42ef5162-5781-427b-ac9d-a152500ed32f
      42ef5162-5781-427b-ac9d-a152500ed32f
      

      发现三组值是一样的, 说明获得的是同一个实例, 在刷新一下页面, 仍然是这三组值, 说明多次请求获得的结果也是同一个实例.

    生命周期

    使用

    • 在Startup类ConfigureService中初始化

      public void ConfigureServices(IServiceCollection services)
          {
              services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
      
              services.AddTransient<ITest, Test>();
          }
      

      方法中默认调用了services.AddMvc(), 是IServiceCollection的一个扩展方法 public static IMvcBuilder AddMvc(this IServiceCollection services), 作用就是向这个清单中添加了一些MVC需要的服务,例如Authorization、RazorViewEngin、DataAnnotations等。

    • Controller中使用

      private ITest _test;
      private ILogger<HomeController> _logger;
      
      public HomeController(ITest test, ILogger<HomeController> logger)
      {
          this._test = test;
          this._logger = logger;
      }
      
    • 通过HttpContext来获取实例
      HttpContext下有一个RequestedService同样可以用来获取实例对象,不过这种方法一般不推荐。需要添加Microsoft.Extension.DependencyInjection的using来调用这个方法的。

      HttpContext.RequestServices.GetService<ITest>()
      
    • View中使用
      在View中通过@inject声明

      @inject ITest TestFromView
      
      <ul>
      <li>@TestFromView.Guid</li>
      </ul>
      

    释放

    对于每次请求, 我们最初配置的根IServiceProvider通过CreateScope()创建了一个新的IServiceScope, 而这个IServiceScope的ServiceProvider属性将负责本次该次请求的服务提供, 当请求结束, 这个ServiceProvider的dispose会被调用。

    在2.0中, ServiceProvider只调用由它创建的 IDisposable 类型的 Dispose。 如果将一个实例添加到容器,它将不会被释放。
    例如:

    services.AddSingleton<ITest>(new Test()); 
    

    替换为其它的 Ioc 容器

    可以将默认的容器改为其他的容器, 比如Autofac, 需要把Startup类里面的 ConfigureService的 返回值从 void改为 IServiceProvider即可。而返回的则是一个AutofacServiceProvider。

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
        // Add other framework services
    
        // Add Autofac
        var containerBuilder = new ContainerBuilder();
        containerBuilder.RegisterModule<DefaultModule>();
        containerBuilder.Populate(services);
        var container = containerBuilder.Build();
        return new AutofacServiceProvider(container);
    }
    
    
    public class DefaultModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<CharacterRepository>().As<ICharacterRepository>();
        }
    }
    

    参考

  • 相关阅读:
    如何解决selenium打开chrome提示chromedriver.exe已停止工作
    移动端弱网测试 fiddle
    android 真机设备识别不出adb interface
    网络基础知识
    《Mongo权威指南》学习手记
    MongoDB数据库备份
    windows下安装mongoDB(zip版)
    ubuntu apt
    docker 免sudo设置(仅3个命令)
    ubuntu18.04双卡机安装ubidia驱动遇到的坑
  • 原文地址:https://www.cnblogs.com/royzshare/p/9440914.html
Copyright © 2011-2022 走看看