一、依赖注入
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