前言

什么是依赖注入?

依赖注入(Dependency Injection),简称DI。

因为某客户类只依赖于服务类的一个接口,而不依赖于具体服务类,所以客户类只定义一个注入点。在程序运行过程中,客户类不间接实例化具体服务类实例,而是客户类的运行上下文环境或专门组件负责实例化服务类,而后将其注入到客户类中,保障客户类的失常运行。

依赖关系反转

应用程序中的依赖关系方向应该是形象的方向,而不是实现详细信息的方向。 大部分应用程序都是这样编写的:编译时依赖关系顺着运行时执行的方向流动,从而生成一个间接依赖项关系图。 也就是说,如果类 A 调用类 B 的办法,类 B 调用 C 类的办法,则在编译时,类 A 将取决于类 B,而 B 类又取决于类 C,如图所示

                                间接关系依赖
利用依赖关系反转准则后,A 能够调用 B 实现的形象上的办法,让 A 能够在运行时调用 B,而 B 又在编译时依赖于 A 管制的接口(因而,典型的编译时依赖项产生反转)。 运行时,程序执行的流程放弃不变,但接口引入意味着能够轻松插入这些接口的不同实现。

                                反转依赖项关系图
依赖项反转是生成涣散耦合应用程序的要害一环,因为能够将实现详细信息编写为依赖并实现更高级别的形象,而不是相同。 因而,生成的应用程序的可测试性、模块化水平以及可维护性更高。 遵循依赖关系反转准则可实现依赖关系注入。

NET 中的依赖关系注入

.NET 反对依赖关系注入 (DI) 软件设计模式,这是一种在类及其依赖项之间实现管制反转 (IoC) 的技术。 .NET 中的依赖关系注入是框架的内置局部。

内置的IOC容器

通过nuget增加援用
Microsoft.Extensions.DependencyInjection
Microsoft.Extensions.DependencyInjection.Abstractions
internal class Program{    private static void Main(string[] args)    {        DbHelper dbHelper = new DbHelper();        dbHelper.DoConnect();    }}public class DbHelper{    private readonly Redis redis = new Redis();    public void DoConnect()    {        redis.Connect();    }}public class Redis{    public void Connect()    {        Console.WriteLine($"HashCode:{this.GetHashCode()}Redis.....Connect");    }}

在下面代码中,类DbHelper创立并间接依赖于Redis类。 硬编码的依赖项会产生问题,应防止应用,起因如下:

  • 要用不同的实现替换 Redis,必须批改 DbHelper 类。
  • 如果 Redis 具备依赖项,则必须由 DbHelper 类对其进行配置。 在具备多个依赖于 Redis 的类的大型项目中,配置代码将扩散在整个利用中。
  • 很难进行单元测试。

通过依赖关系注入形式能够解决以上问题

  • 应用接口或基类将依赖关系实现抽象化。
  • 在服务容器中注册依赖关系。
public interface IDbHelper{    void Connect();}public class Redis : IDbHelper{    public void Connect()    {        Console.WriteLine($"HashCode:{this.GetHashCode()}Redis.....Connect");    }}

IDbHelper 接口定义 Connect 办法并由 Redis 实现

.NET Core中通过IServiceCollection和IServiceProvider两个组件实现依赖注入和管制反转。
IServiceCollection-注册
IServiceProvider-提供实例。

using Microsoft.Extensions.DependencyInjection;internal class Program{    private static void Main(string[] args)    {        IServiceCollection service = new ServiceCollection();        service.AddTransient<IDbHelper, Redis>();        IServiceProvider serviceProvider = service.BuildServiceProvider();        var instance = serviceProvider.GetService<IDbHelper>();        DbHelper dbHelper = new DbHelper(instance);        dbHelper.DoConnect();    }}

生命周期

  • Transient
  • Scoped
  • Singleton

下列各局部形容了上述每个生存期。 为每个注册的服务抉择适当的生存期。

临时---AddTransient

临时生存期服务是每次从服务容器进行申请时创立的。 这种生存期适宜轻量级、 无状态的服务。 在解决申请的利用中,在申请完结时会开释临时服务。

范畴内---AddScoped

对于 Web 利用,指定了作用域的生存期指明了每个客户端申请(连贯)创立一次服务。在解决申请的利用中,在申请完结时会开释有作用域的服务。

单例---AddSingleton

创立单例生命周期服务的状况如下:在首次申请它们时进行创立或者在向容器间接提供实现实例时由开发人员进行创立。来自依赖关系注入容器的服务实现的每一个后续申请都应用同一个实例。 如果利用须要繁多实例行为,则容许服务容器治理服务的生存期。

namespace IOC.Common{    public interface IDbHelper    {        void Connect();    }    public interface ITransient : IDbHelper    {    }    public interface ISingleton : IDbHelper    {    }    public interface IScoped : IDbHelper    {    }    public class SqlServer : ITransient    {        public void Connect()        {            Console.WriteLine($"HashCode:{this.GetHashCode()}SqlServer.....Connect");        }    }    public class MySql : IScoped    {        public void Connect()        {            Console.WriteLine($"HashCode:{this.GetHashCode()}MySql.....Connect");        }    }    public class Oracle : ISingleton    {        public void Connect()        {            Console.WriteLine($"HashCode:{this.GetHashCode()}Oracle.....Connect");        }    }}

下述代码别离应用了

  • AddTransient
  • AddScoped
  • AddSingleton
    注册了不同的服务,通过输入后果能够看出三种生命周期的区别
// See https://aka.ms/new-console-template for more informationusing IOC.Common;using Microsoft.Extensions.DependencyInjection;internal class Program{    private static void Main(string[] args)    {        var services = new ServiceCollection();        //register service        services.AddTransient<ITransient, SqlServer>();        services.AddSingleton<ISingleton, Oracle>();        services.AddScoped<IScoped, MySql>();        var provider = services.BuildServiceProvider();        Console.WriteLine($"**********************************LiftTime:Transient**********************************");        TestTransient(provider);        Console.WriteLine($"**********************************LiftTime:Singleton**********************************");        TestSingleton(provider);        Console.WriteLine($"**********************************LiftTime:Scoped**********************************");        TestScoped(provider);        Console.ReadLine();    }    private static void TestScoped(ServiceProvider provider)    {        for (int i = 0; i < 5; i++)        {            using (var scope = provider.CreateScope())            {                Console.WriteLine($"**********************************Scope:{scope.GetHashCode()}**********************************");                IDbHelper db = scope.ServiceProvider.GetService<IScoped>();                if (db != null)                {                    db.Connect();                }            }        }    }    private static void TestSingleton(ServiceProvider provider)    {        for (int i = 0; i < 5; i++)        {            IDbHelper db = provider.GetService<ISingleton>();            if (db != null)            {                db.Connect();            }        }    }    public static void TestTransient(ServiceProvider provider)    {        for (int i = 0; i < 5; i++)        {            IDbHelper db = provider.GetService<ITransient>();            if (db != null)            {                db.Connect();            }        }    }}

写在最初

本文次要介绍了.NET Core中内置的IoC容器的应用办法
在Microsoft.Extensions.DependencyInjection中只能用构造函数注入