一、依赖注入
1.1、依赖注入简介
依赖:是指一个对象所依赖的另一个对象(即:如果一个类A 的功能实现需要借助于类B,那么就称类B是类A的依赖);

耦合:两个或两个以上的类一起构建出某个功能,若其中一个类发生变化,导致其他依赖它的类也会发生变化(即:如果在类A的内部去实例化类B,那么两者之间会出现较高的耦合,一旦类B出现了问题,类A也需要进行对应的改造,如果这样的情况很多,即每个类之间都有很多的依赖,那么就会出现牵一发而动全身的情况,导致程序会极难维护,十分容易出现问题)。

要解决这个问题,就要把类A对类B的控制权抽离出来,交给一个第三方去做;把控制权反转给第三方,就称作:控制反转(IOC Inversion Of Control),控制反转是一种思想,是能够解决耦合问题的一种可能的结果;

依赖注入(Dependency Injection)就是其最典型的实现方法。由第三方(我们称作IOC容器)来控制依赖,把他通过构造函数、属性或者工厂模式等方法,注入到类A内,这样就极大程度的对类A和类B进行了解耦。

依赖注入的核心思想:将具体类之间的依赖,尽量转换成抽象依赖。

1.2、示例
①定义接口和类

public interface IInterfaceA
{ 
}

public interface IInterfaceB
{ 
}


public class ClassA : IInterfaceA
{
    private IInterfaceB _b;

    public ClassA(IInterfaceB b)
    {
        this._b = b;
    }
}

public class ClassB : IInterfaceB
{ 
}

②此时需要获取IInterfaceA,则进行如下操作

IInterfaceB b = new ClassB();
IInterfaceA a = new ClassA(b);
③这个时候IInterfaceA的控制权,在实例化的时候就已经被限定死了【即:ClassA的实例】,没有任何扩展的余地,并且我们还要手工的初始化IInterfaceB,同样IInterfaceB的控制权也被限定死了。【这样的代码毫无设计、也极不利于扩展】。

1.3、依赖注入示意图
如果采用依赖注入,则代码如下:

var a = container.GetService<IInterfaceA>();
这个时候接口A和B的控制权是由容器来控制的,我们可以通过向容器中注入不同的接口实现来扩展系统的灵活性,由于将控制权交给了IoC容器,我们还可以通过配置的方式灵活的控制对象的生命周期,这一点也是手工创建对象无法实现的。

控制反转的关系图如下(图片来源于体系结构原则 | Microsoft Docs):

如果类 A 调用类 B 的方法,类 B 调用 C 类的方法,则在编译时,类 A 将取决于类 B,而 B 类又取决于类 C,如图 所示。

 

直接依赖关系图

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

 

反转依赖项关系图

二、ASP.NET Core中的依赖注入
Asp.Net Core3.1依赖注入项目工程下载

2.0、编写接口和实现类

public interface IUser
    {
        //根据用户ID获取用户信息
        object GetUserOfId();

    }//Interface_end
    public interface ILog
    {
        /// <summary>
        /// 写入日志信息
        /// </summary>
        /// <param name="level">日志级别</param>
        /// <param name="info">日志信息</param>
        /// <returns>返回写入日志结果</returns>
        bool WriteLog(string level,string info);


    }//Class_end
    public class UserImpl : IUser
    {
        private ILog _Log;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="log"></param>
        public UserImpl(ILog log)
        {
            this._Log = log;
        }

        /// <summary>
        /// 根据ID获取用户信息  
        /// </summary>
        /// <returns></returns>
        public object GetUserOfId()
        {
            string str = "这是没有使用缓存直接读取数据库的用户信息";

            _Log.WriteLog("Normal",str);

            return str;
        }


    }//Class_endusing IService;
using System;
using System.Collections.Generic;
using System.Text;

namespace ConcreteService
{
    public class UserCacheImpl : IUser
    {
        public object GetUserOfId()
        {
            string str = "使用Redies缓存策略获取的用户信息";
            return str;
        }
    }//Class_end

} public class log : ILog
    {
        //写入日志信息
        public bool WriteLog(string level, string info)
        {
            bool result = false;
            string str = $"写入 {level} 的日志信息:{info}";
            if (!string.IsNullOrEmpty(str))
            {
                Console.WriteLine(str);
                result = true;
            }

            return result;
        }
    }//Class_end


2.1、使用自带的依赖注入服务
Asp.NET Core服务注册很简单,即接口和接口实现类一对一,或一对多;有三种Add方法,分别代表不同的生命周期:

AddSingleton:单个实例,生命周期最长。

AddTransient:随用随销毁,生命周期最短。

AddScoped:生命周期在客户端与服务器的单次会话中,只要会话不结束,就不会被销毁。

①在【Startup.cs类的ConfigureServices()方法下】添加实现类和接口的注入依赖关系

public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

            ervices.AddScoped<IUser,UserImpl>();
            services.AddScoped<ILog,log>();

        }


②在控制器(Controller)的使用方法如下:

[Route("api/[controller]/[action]")]
    [ApiController]
    public class UserInfoController : ControllerBase
    {
        private readonly IUser _User;

        public UserInfoController(IUser user)
        {
            _User = user;
        }


        /// <summary>
        /// 根据ID获取用户信息
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public object GetUserInfoOfId()
        {
            不使用依赖注入的实现
            //IUser user = new UserImpl(new log());

            //使用依赖注入实现
            return _User.GetUserOfId();
        }


    }//Class_end

③运行效果

 

2.2、使用第三方依赖注入插件Autofac
《1》添加【Autofac.Extensions.DependencyInjection】的Nuget包

 

《2》添加单个类和接口的依赖

①在Program.cs的CreateHostBuilder()方法中添加:
【.UseServiceProviderFactory(newAutofacServiceProviderFactory())】

using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace Learn_WebApiBase
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                //改用Autofac来实现依赖注入
                .UseServiceProviderFactory(new AutofacServiceProviderFactory())
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

②编写自定义注入模块:【继承Module类,重写Load()方法】

using Autofac;
using ConcreteService;
using IService;
using System;

namespace Learn_WebApiBase.Services
{
    public class CustomAutofacModule:Module
    {
        /// <summary>
        /// Autofac注册类
        /// </summary>
        /// <param name="builder"></param>
        protected override void Load(ContainerBuilder builder)
        {
            //单个内容引用
            builder.RegisterType<UserImpl>().As<IUser>();
            builder.RegisterType<log>().As<ILog>();

        }

    }//Class_end
}

③在Startup.cs类中添加自定义的Autofac注入方法

using Autofac;
using Autofac.Extensions.DependencyInjection;
using ConcreteService;
using IService;
using Learn_WebApiBase.Services;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;

namespace Learn_WebApiBase
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }
        //Autofac新增
        public ILifetimeScope AutofacContainer { get; private set; }

        //Autofac新增
        public void ConfigureContainer(ContainerBuilder builder)
        {
            //直接使用Autofac注册自定义内容
            builder.RegisterModule(new CustomAutofacModule());
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            //Autofac新增可选
            AutofacContainer = app.ApplicationServices.GetAutofacRoot();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }


    }
}

④ 编写控制器(Controller)

[Route("api/[controller]/[action]")]
    [ApiController]
    public class UserInfoController : ControllerBase
    {
        private readonly IUser _User;

        public UserInfoController(IUser user)
        {
            _User = user;
        }


        /// <summary>
        /// 根据ID获取用户信息
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public object GetUserInfoOfId()
        {
            不使用依赖注入的实现
            //IUser user = new UserImpl(new log());

            //使用依赖注入实现
            return _User.GetUserOfId();
        }


    }//Class_end

⑤运行效果:

 

《3》批量注入所有的实现类

①在Program.cs的CreateHostBuilder()方法中添加:
【.UseServiceProviderFactory(newAutofacServiceProviderFactory())】(与上面添加单个类和接口的依赖一样)。

②编写自定义注入模块:【继承Module类,重写Load()方法】

using Autofac;
using ConcreteService;
using IService;
using System;
using System.IO;

namespace Learn_WebApiBase.Services
{
    public class CustomAutofacModule:Module
    {
        /// <summary>
        /// Autofac注册类
        /// </summary>
        /// <param name="builder"></param>
        protected override void Load(ContainerBuilder builder)
        {

            //多内容批量引用
            var assemblyRespositorys = System.Reflection.Assembly.Load("ConcreteService");
            //string basePath = AppContext.BaseDirectory;
            //var assemblyRespositorys = System.Reflection.Assembly.LoadFrom(Path.Combine(basePath, "ConcreteService.dll"));
            builder.RegisterAssemblyTypes(assemblyRespositorys).AsImplementedInterfaces();
        }

    }//Class_end
}

③在Startup.cs类中添加自定义的Autofac注入方法(与上面添加单个类和接口的依赖一样)

④编写控制器(Controller)

using IService;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;

namespace Learn_WebApiBase.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class UserInfoController : ControllerBase
    {
        private readonly IEnumerable<IUser> _userList;

        public UserInfoController(IEnumerable<IUser> userList)
        {
            _userList = userList;
        }


        /// <summary>
        /// 根据ID获取用户信息
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public object GetUserInfoOfId()
        {
            //使用依赖注入实现
            //IUser user = new UserImpl(new log());
            List<object> tmpList = new List<object>();

            foreach (var item in _userList)
            {
               tmpList.Add(item.GetUserOfId());
            }
            return tmpList;
        }


    }//Class_end
}

⑤运行效果:

 

三、思维导图和参考资料
3.1、思维导图

3.2、参考资料
体系结构原则 | Microsoft Docs
https://docs.microsoft.com/zh-cn/dotnet/architecture/modern-web-apps-azure/architectural-principles

ASP.NET Core 依赖注入 | Microsoft Docs
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0

依赖注入视频
https://www.bilibili.com/video/BV1sE411y7ot?p=1

Autofac在Asp.Net Core 中的配置官网
https://docs.autofac.org/en/latest/integration/aspnetcore.html#asp-net-core-3-0-and-generic-hosting