ASP.NET Core 依赖注入分步指南

在这里插入图片描述

面向对象编程是关于类和对象的,为了构建复杂的软件,我们必须创建大量的对象来完成工作。如果您不遵循任何设计模式来控制这些对象的创建和生命周期,那么您很容易陷入一团糟,其中软件中的一个小更改或一个新需求可能会迫使您在整个应用程序中更改大量代码.有许多软件设计模式和原则可以帮助我们编写更灵活、易于管理和易于测试的软件。在本教程中,我将尝试介绍一种称为依赖注入的设计模式,它也是 ASP.NET Core 中的一等公民。我将尝试在一些真实示例的帮助下解释所有内容,以便您可以轻松掌握概念并自信地开始在您的项目中使用依赖注入。

下载:Download Source Code

什么是依赖注入

根据维基百科:

在软件工程中,依赖注入是一种技术,其中一个对象接收它所依赖的其他对象。这些其他对象称为依赖项。” —— 维基百科

上面的维基百科定义不是特别容易理解,所以让我给你一个现实世界的例子,它与依赖注入密切相关。

假设您在酒店房间内并且感到饥饿。你会开始自己做早餐吗?我不这么认为。我们在酒店通常做的是打电话给客房服务或酒店前台,并要求他们将早餐送到我们的房间。我们饿了,我们依靠酒店服务/工作人员为我们提供早餐。我们不在乎早餐是怎么做的。我们甚至不知道是谁在酒店厨房里做我们的早餐。早餐准备好后,酒店客房服务将为我们提供早餐。
在这里插入图片描述
现在将上面的真实示例映射到编程世界。当A类使用B类的某些功能时,就说A类依赖B类。要调用B类的任何方法,我们可以在A类内部创建B类的对象,并可以使用B类的功能容易地。假设一段时间后需求发生变化,现在您需要用一个名为 C 类的新类替换 B 类的功能。您现在遇到了大麻烦,因为您直接在代码的许多不同位置创建了 B 类对象,现在您必须到处更改代码。您的类是紧密耦合的,您不能在不更改代码的情况下用类 C 替换 B 类功能。

依赖注入是一种使类独立于其依赖项的编程技术。我们不会直接在我们的代码中创建其他类(依赖项)的对象,而是要求其他人(DI 容器)为我们创建对象,一旦创建了这些对象,它们就会被提供(注入)到我们的类中,以便我们可以使用它们在代码中。在这里插入图片描述

依赖注入的好处

以下是在代码中使用依赖注入的一些优势。

  1. 灵活且可维护的代码:它使我们的代码更加灵活和可维护,因为我们可以在不更改应用程序中的代码或业务逻辑的情况下更改类的实现。代码也很容易阅读,因为依赖项的初始化或创建是在代码之外完成的。
  2. 简单的单元测试:我们可以通过注入一个或另一个类来轻松测试不同的实现。
  3. 松散耦合:依赖注入促进了软件组件的松散耦合。
  4. 易于扩展的应用程序:依赖注入可以轻松扩展我们的应用程序,因为我们不仅可以更轻松地引入新组件,而且还可以实现现有组件的更好版本并将它们轻松注入到我们的应用程序中。

ASP.NET Core 中的依赖注入

过去,我们使用 AutofacCastle WindsorUnity 等库和框架在我们的项目中实现依赖注入,但依赖注入现在是 ASP.NET Core 的一部分。所有框架服务,如配置、日志记录、路由等,现在都注册在一个内置的 DI 容器中,并在我们需要的任何地方作为服务提供给我们。基本思想是在应用程序启动时注册所有服务(依赖项),然后这些服务将在运行时注入和解析。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    { 
        services.AddTransient<IProductService, ProductService>();
        services.AddScoped<ILoggingService, LoggingService>();
        services.AddSingleton<ICacheProvider, CacheProvider>();
    }
}

ASP.NET Core 依赖注入框架及其相关类在 Microsoft.Extensions.DependencyInjection 命名空间中可用,两个最重要的组件如下:
IServiceCollection:此接口是在 .NET Core 应用程序中使用依赖注入的标准方式。我们使用这个接口来注册我们想要解析和稍后在我们的应用程序中注入的所有服务。启动类(如上所示)提供了 ConfigureServices 方法,其中 IServiceCollection 的实例可用作参数。
IServiceProvider:该接口用于通过实际查找哪个接口属于哪个具体实现并进行创建来解析服务实例。

ASP.NET Core 服务生命周期

服务生命周期是指服务在被垃圾收集之前将存活多长时间。某些服务将在短时间内实例化,并且仅在特定组件和请求中可用。有些将仅实例化一次,并且在整个应用程序中都可用。以下是 ASP.NET Core 中可用的服务生命周期。

Singleton (单例)

服务类的单个实例被创建,存储在内存中,并在整个应用程序中重复使用。我们可以将 Singleton 用于实例化成本很高的服务。我们还可以在希望跨多个组件共享状态的场景中使用它。需要考虑的一个重点是线程安全,因为实例只创建一次并被许多消费者访问。我们可以使用 AddSingleton 方法注册 Singleton 服务,如下所示:

services.AddSingleton<IProductService, ProductService>();

Scoped (作用域)

每个请求都会创建一次服务实例。参与处理单个请求的所有中间件、MVC 控制器等都将获得相同的实例。范围服务的一个很好的候选者是实体框架上下文。我们可以使用 AddScoped 方法注册 Scoped 服务,如下所示:

services.AddScoped<IProductService, ProductService>();

Transient (瞬时)

每次请求时都会创建瞬态生命周期服务。这个生命周期最适合轻量级、无状态的服务。当您不知道要使用哪个生命周期时,这是一个很好的默认选择,因为每个使用者都会获得服务的副本,并且线程安全不会成为问题。我们可以使用 AddTransient 方法注册 Transient 服务,如下所示:

services.AddTransient<IProductService, ProductService>();

如果您想可视化上述概念,那么这里有一个信息图供您快速参考。
在这里插入图片描述

ASP.NET Core 依赖注入示例

现在是时候看看在 ASP.NET Core 中使用依赖注入的一些实际示例了。创建一个 ASP.NET Core MVC Web 应用程序并在 Models 文件夹中创建以下产品类。

public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

创建文件夹 Services 并使用单个 GetProducts 方法添加接口 IProductService。

public interface IProductService
{
    List<Product> GetProducts();
}

接下来,创建一个名为 AmazonProductService 的类并在该类上实现 IProductService 接口。在本教程中,我没有使用任何后端存储库或服务从数据库加载产品,所以为了简单起见,让我们从 GetProducts 方法返回一些硬编码的产品,如下所示:

public class AmazonProductService : IProductService
{
    public List<Product> GetProducts()
    {
        return new List<Product>()
        {
            new Product() { Id = 1001, Name = "Apple AirPods Pro", Price = 249.00m },
            new Product() { Id = 1002, Name = "Sony Noise Cancelling Headphones", Price = 199.00m },
            new Product() { Id = 1003, Name = "Acer Aspire 5 Slim Laptop", Price = 346.00m } 
        };
    }
}

接下来,我们需要在 Startup 类中注册我们的服务,如下所示:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    { 
        services.AddTransient<IProductService, AmazonProductService>(); 
    }
}

接下来,我们需要将我们的服务注入控制器。我们可以在控制器的构造函数中注入服务,如下所示:

public class HomeController : Controller
{
    private readonly IProductService _productService;
    public HomeController(IProductService productService)
    {
        _productService = productService;
    }
    public IActionResult Index()
    {
        var products = _productService.GetProducts();
        return View(products);
    }
}

最后,我们可以在 Index.cshtml razor 视图文件中显示产品,如下所示:

@model List<Product>
@{
    ViewData["Title"] = "Home Page";
}
<br />
<br />
<div>
    <h3 class="text-center">ASP.NET Core Dependency Injection</h3>
    <br />
    <table class="table">
        <thead class="thead-dark">
            <tr>
                <th>Id</th>
                <th>Name</th>
                <th>Price</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var product in Model)
            {
                <tr>
                    <td>@product.Id</td>
                    <td>@product.Name</td>
                    <td>@product.Price</td>
                </tr>
            }
        </tbody>
    </table>
</div>

运行应用程序,您应该能够看到从 AmazonProductService 返回的所有产品。这是因为在运行时,当我们的 Home Controller 请求实现 IProductService 的类的实例时,依赖注入框架将其解析为在 Startup.cs 类中注册的 AmazonProductService
在这里插入图片描述
假设您的应用程序需求发生变化,并且您突然决定应该从 Ebay 而不是 Amazon 加载产品。您可以创建另一个类 EbayProductService,它实现相同的 IProductService 接口并拥有自己的 GetProducts 方法实现。

public class EbayProductService : IProductService
{
    public List<Product> GetProducts()
    {
        return new List<Product>()
        {
            new Product() { Id = 2001, Name = "Apple iPhone XS Max", Price = 660.00m },
            new Product() { Id = 2002, Name = "Apple iPhone 7", Price = 134.00m },
            new Product() { Id = 2003, Name = "Sony Cyber Shot Camera", Price = 109.00m }
        };
    }
}

您不必更改应用程序中的任何一行代码。您只需在 Startup.cs 文件中注册 EbayProductService 即可。

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    { 
        services.AddTransient<IProductService, EbayProductService>(); 
    }
}

依赖于 IProductService 的控制器和视图将自动开始显示 Ebay 产品而不是亚马逊产品。在这里插入图片描述

在 DI 容器中动态注册服务

假设您想根据环境使用两种不同的服务。您想在开发环境中测试 Amazon 服务,但您想在生产环境中使用 Ebay 服务。您可以通过在 Startup 类的构造函数中注入 IWebHostEnvironment 轻松实现这一点,然后您可以动态注册服务,如下所示。

public class Startup
{
    private IWebHostEnvironment _env;
 
    public Startup(IWebHostEnvironment env)
    {
        _env = env;
    }
 
    public void ConfigureServices(IServiceCollection services)
    {
        if (_env.IsProduction())
        {
            services.AddTransient<IProductService, EbayProductService>();
        }
        else
        {
            services.AddTransient<IProductService, AmazonProductService>();
        } 
    }
}

总结概括

在本教程中,我试图阐明与依赖注入设计模式相关的主要概念。已经概述了 .NET Core 提供的支持依赖注入的基础结构。我希望您现在已经学会了如何配置 ASP.NET Core 应用程序以使用内置 IoC 容器和注册服务。同时希望您还了解了 .NET 核心应用程序中可用的不同服务生命周期。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值