热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

深入理解ASP.NETCore依赖注入

概述ASP.NETCore可以说是处处皆注入,本文从基础角度理解一下原生DI容器,及介绍下怎么使用并且如何替换官方提供的默认依赖注入容器。什么是依赖注入百度百科中对于依赖注入的定义

概述

       ASP.NET Core可以说是处处皆注入,本文从基础角度理解一下原生DI容器,及介绍下怎么使用并且如何替换官方提供的默认依赖注入容器。

什么是依赖注入

       百度百科中对于依赖注入的定义:控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

依赖反转前

那么在依赖反转之前或者叫控制反转之前,直接依赖是怎么工作的呢,这里ClassA直接依赖ClassB,而ClassB又直接依赖ClassC,任何一处的变动都会牵一发而动全身,不符合软件工程的设计原则。

依赖反转后

应用依赖关系反转原则后,A 可以调用 B 实现的抽象上的方法,让 A 可以在运行时调用 B,而 B 又在编译时依赖于 A 控制的接口(因此,典型的编译时依赖项发生反转) 。 运行时,程序执行的流程保持不变,但接口引入意味着可以轻松插入这些接口的不同实现。

依赖项反转是生成松散耦合应用程序的关键一环,因为可以将实现详细信息编写为依赖并实现更高级别的抽象,而不是相反 。 因此,生成的应用程序的可测试性、模块化程度以及可维护性更高。 遵循依赖关系反转原则可实现依赖关系注入 。

何谓容器

       如果你用过Spring,就知道其庞大而全能的生态圈正是因为有了它包含各种各样的容器来做各种事情,其本质也是一个依赖反转工厂,那么不知道你注意到没有,控制反转后产生依赖注入,这样的工作我们可以手动来做,那么如果注入的服务成千上万呢,那怎么玩呢?那么问题来了,控制反转了,依赖的关系也交给了外部,现在的问题就是依赖太多,我们需要有一个地方来管理所有的依赖,这就是容器的角色。
      容器的主要职责有两个:绑定服务与实例之间的关系(控制生命周期)获取实例并对实例进行管理(创建和销毁)

ASP.NET Core里依赖注入是怎么实现的

     在.Net Core里提供了默认的依赖注入容器IServiceCollection,它是一个轻量级容器。核心组件为两个IServiceCollection和IServiceProvider,IServiceCollection负责注册,IServiceProvider负责提供实例。
      使用两个核心组件前导入命名空间Microsoft.Extensions.DependencyInjection.
      默认的ServiceCollection有以下三个方法:

IServiceCollection serviceCollection=new ServiceCollection();
#三个方法都是注册实例,只不过实例的生命周期不一样。
#单例模式,只有一个实例
serviceCollection.AddSingleton();
#每次请求都是同一个实例,比如EntityFramework.Context
serviceCollection.AddScoped();
#每次调用都是不同的实例
serviceCollection.AddTransient();
#接口声明
public interface IServiceCollection : IList, ICollection, IEnumerable, IEnumerable
{
}
#默认的ServiceCollection实际上是一个提供了ServiceDescriptor的List。 
public class ServiceCollection : IServiceCollection, ICollection, IEnumerable, IEnumerable, IList
  {
    private readonly List _descriptors = new List();

    public int Count
    {
      get
      {
        return this._descriptors.Count;
      }
    }
    
    public bool IsReadOnly
    {
      get
      {
        return false;
      }
    }

    public ServiceDescriptor this[int index]
    {
      get
      {
        return this._descriptors[index];
      }
      set
      {
        this._descriptors[index] = value;
      }
    }

    public void Clear()
    {
      this._descriptors.Clear();
    }

    public bool Contains(ServiceDescriptor item)
    {
      return this._descriptors.Contains(item);
    }
    
    public void CopyTo(ServiceDescriptor[] array, int arrayIndex)
    {
      this._descriptors.CopyTo(array, arrayIndex);
    }

    public bool Remove(ServiceDescriptor item)
    {
      return this._descriptors.Remove(item);
    }

    public IEnumerator GetEnumerator()
    {
      return (IEnumerator) this._descriptors.GetEnumerator();
    }

    void ICollection.Add(ServiceDescriptor item)
    {
      this._descriptors.Add(item);
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
      return (IEnumerator) this.GetEnumerator();
    }

    public int IndexOf(ServiceDescriptor item)
    {
      return this._descriptors.IndexOf(item);
    }
    
    public void Insert(int index, ServiceDescriptor item)
    {
      this._descriptors.Insert(index, item);
    }

    public void RemoveAt(int index)
    {
      this._descriptors.RemoveAt(index);
    }
  }

三个方法对应的生命周期值,在枚举ServiceLifeTime中定义:

public enum ServiceLifetime
{
   Singleton, 
   Scoped,
   Transient,
}

三个方法确切来说是定义在扩展方法ServiceCollectionServiceExtensions中定义:

#这里我列出个别方法,详细可参看源码
#导入Microsoft.Extensions.DependencyInjection
public static class ServiceCollectionServiceExtensions
{   
    public static IServiceCollection AddTransient(
      this IServiceCollection services,
      Type serviceType,
      Type implementationType)
    {
      if (services == null)
        throw new ArgumentNullException(nameof (services));
      if (serviceType == (Type) null)
        throw new ArgumentNullException(nameof (serviceType));
      if (implementatiOnType== (Type) null)
        throw new ArgumentNullException(nameof (implementationType));
      //这里注入时指定ServiceLifetime.Transient
      return ServiceCollectionServiceExtensions.Add(services, serviceType, implementationType, ServiceLifetime.Transient);
    }
    
    public static IServiceCollection AddScoped(
      this IServiceCollection services,
      Type serviceType,
      Type implementationType)
    {
      if (services == null)
        throw new ArgumentNullException(nameof (services));
      if (serviceType == (Type) null)
        throw new ArgumentNullException(nameof (serviceType));
      if (implementatiOnType== (Type) null)
        throw new ArgumentNullException(nameof (implementationType));
      //这里注入时指定ServiceLifetime.Scoped
      return ServiceCollectionServiceExtensions.Add(services, serviceType, implementationType, ServiceLifetime.Scoped);
    }
    
    public static IServiceCollection AddSingleton(
      this IServiceCollection services,
      Type serviceType,
      Type implementationType)
    {
      if (services == null)
        throw new ArgumentNullException(nameof (services));
      if (serviceType == (Type) null)
        throw new ArgumentNullException(nameof (serviceType));
      if (implementatiOnType== (Type) null)
        throw new ArgumentNullException(nameof (implementationType));
      //这里注入时指定ServiceLifetime.Singleton
      return ServiceCollectionServiceExtensions.Add(services, serviceType, implementationType, ServiceLifetime.Singleton);
    }
}

ASP.NET Core里依赖注入是怎样运行的

在Startup中初始化

ASP.NET Core在Startup.ConfigureService中注入指定服务,可以从方法参数IServiceCollection中看出,这里还有个方法services.AddMvc(), 这个MVC框架本身自己注入的服务,定义在MvcServiceCollectionExtesnsions类中。

#Startup
public void ConfigureServices(IServiceCollection services)
{
     services.AddMvc();
     services.AddSingleton();        
}

在构造函数中注入

官方推荐在构造器中注入,这里也是为了体现显示依赖。

public class AccountController
{
    private ILoginService _loginService;
    public AccountController(ILoginService loginService)
    {
        _loginService = loginService;
    }
}

如何替换其他容器

      前面提到原生的依赖注入容器只是一个轻量级容器,但是功能真的很有限,比如属性注入、方法注入、子容器、lazy对象初始化支持。为何不好好借鉴一下Spring强大的背景呢,所以这里我们用Autofac替换系统默认的依赖注入容器。先引用命名空间Autofac、Autofac.Extensions.DependencyInjection。我本机环境使用的.Net Core3.0。 3.0不能修改直接修改Startup的ConfigureService方法了,直接修改ConfigureService方法返回值会抛出异常ConfigureServices returning an System.IServiceProvider isn't supported. 这里可以参考Autofac文档,已经有说明。

修改Startup

#直接声明方法ConfigureContainer 
public class Startup
 {
     public Startup(IConfiguration configuration)
     {
         COnfiguration= configuration;
     }

     public IConfiguration Configuration { get; }

     public void ConfigureServices(IServiceCollection services)
     {
         services.AddControllersWithViews();
         services.AddMvc();
     }

     public void ConfigureContainer(ContainerBuilder containerBuilder)
     {
         containerBuilder.RegisterType().As();
     }

     public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
     {
         if (env.IsDevelopment())
         {
             app.UseDeveloperExceptionPage();
         }
         else
         {
             app.UseExceptionHandler("/Home/Error");
             app.UseHsts();
         }

         app.UseStaticFiles();
         app.UseRouting();
         app.UseAuthorization();
         app.UseEndpoints(endpoints =>
                          {
                              endpoints.MapControllerRoute(
                                  name: "default",
                                  pattern: "{cOntroller=Home}/{action=Index}/{id?}");
                          });
     }
 }

修改Program

#导入命名空间Autofac.Extensions.DependencyInjections,然后调用UseServiceProviderFactory 
public class Program
 {
     public static void Main(string[] args)
     {
         CreateHostBuilder(args).Build().Run();
     }

     public static IHostBuilder CreateHostBuilder(string[] args) =>
         Host.CreateDefaultBuilder(args)
         .ConfigureWebHostDefaults(
         webBuilder => { webBuilder.UseStartup(); })
         .UseServiceProviderFactory(new AutofacServiceProviderFactory());
 }

参考链接

https://docs.microsoft.com/zh-cn/dotnet/architecture/modern-web-apps-azure/architectural-principles#dependency-inversion
https://www.cnblogs.com/loogn/p/10566510.html
https://www.cnblogs.com/jesse2013/p/di-in-aspnetcore.html


推荐阅读
  • 解决Bootstrap DataTable Ajax请求重复问题
    在最近的一个项目中,我们使用了JQuery DataTable进行数据展示,虽然使用起来非常方便,但在测试过程中发现了一个问题:当查询条件改变时,有时查询结果的数据不正确。通过FireBug调试发现,点击搜索按钮时,会发送两次Ajax请求,一次是原条件的请求,一次是新条件的请求。 ... [详细]
  • ABP框架是ASP.NET Boilerplate的简称,它不仅是一个开源且文档丰富的应用程序框架,还提供了一套基于领域驱动设计(DDD)的最佳实践架构模型。本文将详细介绍ABP框架的特点、项目结构及其在Web API优先架构中的应用。 ... [详细]
  • 申请地址:https://developer.apple.com/appstore/contact/?topic=expedite 常见申请理由:1. 我们即将发布新产品,这是一个媒体活动,我们无法承担任何风险,因此在多个方面努力提升应用质量。 ... [详细]
  • 本文详细介绍了在 CentOS 7 系统中配置 fstab 文件以实现开机自动挂载 NFS 共享目录的方法,并解决了常见的配置失败问题。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • 开发笔记:前端之前端初识
    开发笔记:前端之前端初识 ... [详细]
  • 一个转子曲线面积问题及其反问题的解答
    曾经解答过这样一个问题,从该ID的最后一次登录时间、该ID显示的专业信息,误以为是新闻里某个想不开的同学,不安了一阵子。经确认是我多虑了,不过把问题答案还是写出来。之后就收到一堆要求帮忙算 ... [详细]
  • ipsec 加密流程(二):ipsec初始化操作
    《openswan》专栏系列文章主要是记录openswan源码学习过程中的笔记。Author:叨陪鲤Email:vip_13031075266163.comDate:2020.1 ... [详细]
  • vsftpd配置(虚拟用户、匿名用户登录)
    一、ftp服务搭建(一)概述1.ftp连接及传输模式(1)控制连接TCP21,用于发送FTP命令信息 ... [详细]
  • Linux 防火墙与端口管理必备命令
    在使用 Linux 系统进行服务部署和问题排查时,防火墙和端口管理是不可或缺的操作。本文将详细介绍如何查看防火墙状态、端口占用情况,以及如何开放和关闭端口,帮助初学者更好地掌握这些技能。 ... [详细]
  • packagecom.panchan.tsmese.utils;importjava.lang.reflect.ParameterizedType;importjava.lang. ... [详细]
  • LeetCode 1736: 替换隐藏数字获取最晚时间
    本文详细介绍了如何通过替换隐藏数字来获取最晚时间,适用于 LeetCode 1736 题目。 ... [详细]
  • 本文详细介绍了Java代码分层的基本概念和常见分层模式,特别是MVC模式。同时探讨了不同项目需求下的分层策略,帮助读者更好地理解和应用Java分层思想。 ... [详细]
  • 基于iSCSI的SQL Server 2012群集测试(一)SQL群集安装
    一、测试需求介绍与准备公司计划服务器迁移过程计划同时上线SQLServer2012,引入SQLServer2012群集提高高可用性,需要对SQLServ ... [详细]
  • 本文详细介绍了 PHP 中对象的生命周期、内存管理和魔术方法的使用,包括对象的自动销毁、析构函数的作用以及各种魔术方法的具体应用场景。 ... [详细]
author-avatar
artiga靜嵐_524
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有