ASP.NET Core基础之依赖注入

阅读本文你的收获:

  1. 了解面试中常被问到的IOC和DI的理论知识
  2. 了解ASP.NET Core中内置的依赖注入机制
  3. 学会使用第三方Autofac进行批量依赖注入

一、控制反转(IOC)与依赖注入(DI)

控制反转(Inversion of Control,缩写为IoC),IOC不是一种技术,而是一种设计思想。它是一个重要的面向对象编程的法则,它能指导我们如何设计出松耦合、更优良的程序。

为什么要控制反转?(IOC)

传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;

有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

用图例对比一下:

没有使用IOC思想的时候,都是主动去创建相关的对象,然后再组合起来,如下图所示:

当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如下图:

如何实现控制反转(IOC)

实现IOC的最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。

DI—Dependency Injection,即“依赖注入”组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

  理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”,那我们来深入分析一下:

  ●谁依赖于谁:当然是应用程序依赖于IoC容器

  ●为什么需要依赖:应用程序需要IoC容器来提供对象需要的外部资源

  ●谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象

  ●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)

二、ASP.NET Core中内置的依赖注入机制

ASP.NET Core 支持依赖关系注入 (DI) 软件设计模式,这是一种在类及其依赖关系之间实现控制反转IOC的技术。

什么是依赖?

当一个类需要另一个类协作来完成工作的时候就产生了依赖,这里有一个设计原则:依赖于抽象,而不是具体的实现

如上图:AccountController就有一个ILoginService的依赖

什么是注入?

把依赖的创建丢给其它人,自己只负责使用,其它人丢给你依赖的这个过程理解为注入

//如果没有 使用依赖注入,则需要自己new实例化所依赖的对象
private ILoginService<ApplicationUser> _loginService;
public AccountController()
{
  _loginService = new EFLoginService();
}
//如果使用依赖注入,通过构造函数让外界把这两个依赖传给你
private ILoginService<ApplicationUser> _loginService;
public AccountController(ILoginService<ApplicationUser> loginService)
{
  _loginService = loginService;
}

.NET Core中的DI有三个核心方法

在.NET Core中DI的核心分为两个组件:IServiceCollection和 IServiceProvider。

  • IServiceCollection 负责注册

  • IServiceProvider 负责提供实例

IServiceCollection 接口有三个扩展方法(在Microsoft.Extensions.DependencyInjection命名空间下),分别用于三种生命周期的实例注册:

  • AddSingleton<, >(), 单例模式,整个应用程序之内只有一个实例(单例)

  • AddTransient<, >(), 瞬态模式,一次性的,需要了实例化,用完了丢弃;

  • AddScoped<, >(), 域模式,在用户请求的范围之内,只产生一个实例。不用的请求又不同的实例。

.NET Core中DI注册的服务有三种生命周期

  • Transient: 瞬时模式、每一次GetService都会创建一个新的实例

  • Scoped: 域模式、在同一个请求域内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http request会在一个 scope内)

  • Singleton :单例模式、整个应用程序生命周期以内只创建一个实例

高频问题:这三种服务周期的区别

权重:AddSingleton→AddTransient→AddScoped

AddSingleton的生命周期:项目启动-项目关闭 相当于静态类 只会有一个

AddScoped的生命周期:请求开始-请求结束 在这次请求中获取的对象都是同一个

AddTransient的生命周期:请求获取-(GC回收-主动释放) 每一次获取的对象都不是同一个

如何完成依赖注入

1. 首先编写相关的接口和实现类,并在Startup.cs的ConfigureService方法中注册服务

//在Startup.cs中,完成服务的注册
services.AddTransient<IOrderService, OrderService>();     //注入订单Service
services.AddTransient<IGoodsService, GoodsService>();     //注入商品Service
services.AddScoped<IOrderRepository, OrderRepository>();  //注入订单的仓储服务

2. 实现依赖注入的方式有三种

  • 接口 + 构造函数 (强制依赖注入)
public class GoodsService : IGoodsService
{
    //代码略
}

//控制器中
[Route("api/[controller]/[action]")]
[ApiController]
public class GoodsController : ControllerBase
{
    //定义一个接口字段
    private readonly IGoodsService _goodsService;
    //构造函数注入该接口对象
    public GoodsController(IGoodsService goodsService)
    {
        this._goodsService = goodsService;
    }
}
  • 属性 + 构造函数注入(可选依赖注入)

//Startup.cs
public class Startup
​
    //构造函数注入该属性
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }
    //定义一个属性
    public IConfiguration Configuration { get; }
}
  • 方法注入

首先,Startup类的Configure方法采用了方法注入的方式,Configure方法的参数可以是任何已经注册的服务类型。

public void Configure(IApplicationBuilder app, 
                      IWebHostEnvironment env /*其他已注册类型的接口*/)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        app.UseSwagger();
        app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json",
                                                     "XfTech.ApiServer v1"));
    }
​
    //静态文件中间件
    app.UseStaticFiles();
​
    ...
}

在方法中,用[FromServices] 获取对应的服务,如:

//商品信息
[HttpGet]
public IActionResult Get(int id, [FromServices] IGoodsService _goodsService)
{
    var result = _goodsService.GetById(id);
    return Ok(result);
}

三、ASP.NET Core中使用第三方Autofac依赖注入框架

.NET Core 自带的DI容器已经很强大了,适合中小型项目,大项目如果不嫌一个一个注册服务麻烦的话,也可以用。如果想要批量注入依赖或一些特殊的情况下,我们需要用到第三方的IOC容器,其中Autofac是最常用的。

Autofac 是一个轻量级的依赖注入(DI)框架,它可以帮助 .NET 开发人员更好地管理对象的生命周期和依赖关系。Autofac 可以大大简化应用程序中对象之间的耦合,使代码更加可维护和可测试。

以下是Autofac的简单示例:

在.NET5中使用Autofac

//下载并引用nuget包
using Autofac.Extensions.DependencyInjection;
​
//在Program.cs中,增加 UseServiceProviderFactory(new AutofacServiceProviderFactory())
public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host.CreateDefaultBuilder(args)
        .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //使用Autofac第三方依赖注入容器
        .ConfigureWebHostDefaults(webBuilder =>
                                  {
                                      webBuilder.UseStartup<Startup>();
                                  })
        .UseNLog(); //使用Nlog日志服务
}
​
//-------------------------------
//在StartUp.cs中增加如下方法
//配置IOC容器
public void ConfigureContainer(ContainerBuilder builder)
{
    #region //1简单的使用--- 与默认DI容器相同的功能
        //生命周期:默认,也就是瞬时模式==AddTransient
        // services.AddScoped<IStudent, Student>();  --这个是微软自带的写法
        //builder.RegisterType<OrderService>().As<IOrderService>();
        生命周期:默认,也就是瞬时模式==AddTransient
        //builder.RegisterType<Student>().As<IStudent>().InstancePerDependency();
        生命周期:Scope模式==AddScoped 
        //builder.RegisterType<Student>().As<IStudent>().InstancePerLifetimeScope();
        生命周期:单例,AddSingleton
        //builder.RegisterType<Student>().As<IStudent>().SingleInstance();
​
    //AutoFac批量注册OrderService所在程序集中所有Service类
    builder.RegisterAssemblyTypes(typeof(OrderService).Assembly)
        .Where(t => t.Name.EndsWith("Service"))
        .AsImplementedInterfaces()
        .InstancePerDependency();  //瞬态模式
​
    //AutoFac批量注册XfTech.IOT9Test.EFCore"中的所有Repository仓储类
    builder.RegisterAssemblyTypes(Assembly.Load("XfTech.EFCore"))
        .Where(t => t.Name.EndsWith("Repository"))
        .AsImplementedInterfaces()
        .InstancePerLifetimeScope();  //域模式
​
    //AutoFac批量注册泛型仓储
    builder.RegisterGeneric(typeof(EfBaseRepository<>))
        .AsImplementedInterfaces()
        .InstancePerLifetimeScope();
​
    #endregion
}

在.NET6中使用Autofac

//下载并引用nuget包
using Autofac.Extensions.DependencyInjection;
​
//在Program.cs中,追加以下代码
​
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); //注册Autofac容器,不注册就用不了
//配置容器
builder.Host.ConfigureContainer<ContainerBuilder>((context, builder) =>
{
    //注册 你所指定的程序集中的所有(非泛型的)类型
    builder.RegisterAssemblyTypes(Assembly.Load("XXX.Application"))
        .Where(a => a.Name.EndsWith("Service"))  //过滤条件,只注册Service结尾的类型
        .AsImplementedInterfaces()   //接口注入
        .InstancePerLifetimeScope();  //三种生命周期 之 域模式Scope
​
    //注入所有的仓储
    builder.RegisterAssemblyTypes(Assembly.Load("XXX.EFCore"))
        .Where(a => a.Name.EndsWith("Repository"))
        .AsImplementedInterfaces()
        .InstancePerLifetimeScope();
​
    //注册所有的泛型类(GenericTypes)   
    builder.RegisterAssemblyOpenGenericTypes(Assembly.Load("XXX.EFCore"))
        .AsImplementedInterfaces()
        .InstancePerLifetimeScope();
});

Autofac 的架构主要分为两个部分:注册器(ContainerBuilder)和容器(IContainer)。注册器用于注册应用程序中所有需要注入的服务和组件,而容器则用于创建和管理这些组件实例

Autofac 的主要特点

轻量级:Autofac 是一个非常轻量级的框架,其核心库只有几个 DLL 文件。这意味着它可以很容易地与其他框架集成,并且对应用程序的性能没有任何影响。

灵活性:Autofac 提供了多种不同的注册方式,如 XML 配置文件、代码配置和属性注解等。开发人员可以根据自己的需求选择最适合的注册方式。

高性能:由于 Autofac 是一个轻量级框架,它的性能非常高。在实例化对象时,Autofac 可以比其他 DI 框架更快地找到并创建所需的依赖项。

生命周期管理:Autofac 提供了多种不同的生命周期管理选项,如瞬态(Transient)、作用域(Scoped)和单例(Singleton)等。这使得开发人员可以更好地控制对象的生命周期,有效地降低内存使用和提高性能。

AOP 支持:Autofac 可以轻松地与 AOP 框架集成,如 Castle DynamicProxy 等。这使得开发人员可以很容易地实现诸如事务管理、缓存和验证等方面的横切关注点。

关于Autofac与AOP框架的集成,将在另一篇博文中进行示例讲解。


参考连接:

学习ASP.NET Core,你必须了解无处不在的“依赖注入”

全面理解 ASP.NET Core 依赖注入

.Net 5.0中使用AutoFac作为IoC容器组件

Autofac 是一个轻量级的依赖注入(DI)框架

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

采石之人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值