热门标签 | 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


推荐阅读
  • Swoole是针对PHP的生产级异步编程框架。它是一种用纯C语言编写的PHP扩展,它使PHP开发人员能够在PHP中编写高性能,可扩展的并发TCP ... [详细]
  • 【实践】基于RTThread的智慧路灯案例实验分享
    之前分享了基于LiteOS的智慧农业案例实验分享基于LiteOS的智慧农业案例实验分享,阅读量挺不错,看样子大家都挺喜欢这种实验。那咱们就再来一个类似的实验:基于RT-Thread ... [详细]
  • 1、对于List而言,要不然就使用迭代器,要不然就从后往前删除,从前往后删除会出现角标越界。因为我List有两个remove方法,一个是int作为形参(删除指定位置的元素),一个是 ... [详细]
  • vector:在vc6中,如果要镶嵌使用vector,如vector,后面的两个应该用,空格隔开,否则被编译器认为是移位符string::npos的值为 ... [详细]
  • 安全3AAuthentication:认证Authorzation:授权Accouting|Audition:审计用户管理用户:UID:0,不一定是root,root的uid非0时 ... [详细]
  • c语言自定义BOOL函数C语言没有BOOL类型变量boolean类型是C++所独有的由于使用BOOL类型可以使代码更具有可读性,很多编程者都在C中自己定义了类似的应用,一般方法有两 ... [详细]
  • UDP协议开发
    UDP是用户数据报协议(UserDatagramProtocol,UDP)的简称,其主要作用是将网络数据流量压缩成数据报形式,提供面向事务的简单信息传送服务。与TCP协议不同,UD ... [详细]
  • 配置OracleACFS集群文件系统
    配置OracleACFS集群文件系统               2012-07-1010:18:39标签:asmacfs版权声明:原创作品,谢绝转载!否则将追究法律责任。     ... [详细]
  • C语言是一个什么样的语言,为什么每个程序员都要学习
    对于大部分程序员,C语言是学习编程的第一门语言,很少有不了解C的程序员。C语言除了能让你了解编程的相关概念,带你走进编程的大门,还能让你明白程序的运行原理,比如,计算机的各个部件是 ... [详细]
  • 开发环境_VS2013+WDK8.1 驱动开发环境搭建
    本文由编程笔记#小编为大家整理,主要介绍了VS2013+WDK8.1驱动开发环境搭建相关的知识,希望对你有一定的参考价值。WindowsDriverKit是一种完全集成 ... [详细]
  • phpmysql一键安装包,Mysql安装包
    本文目录一览:1、哪里有PHPMySQLDatabaseApacheServer一 ... [详细]
  • [ToneTuneToolkit][022]
    #regionEnvironmentWindows1021H1Unity2020.3.30f1c1LTSVSCode1.67.2ToneTuneToolkit下载地址 https: ... [详细]
  • 《ASP.NET MVC 4 实战》 1.3  ASP.NET MVC 3/4的新特性
    本节书摘来自异步社区《ASP.NETMVC4实战》一书中的第1章,第1.3节,作者:【美】JeffreyPalermo,【美】JimmyB ... [详细]
  • cp.php这四个,那么我们如何根据你访问的URL判断涉及到那些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社区 版权所有