.NET Core 中依赖项注入的幕后情况

科技   2024-10-19 04:46   上海  


你知道如何使用 .NET Core 中的接口注入服务。您可能已经无数次编写了这样的代码:

public class ItemsController : ControllerBase  
{
private readonly IItemService _itemService;

public ItemsController(IItemService itemService)
{
_itemService = itemService;
}

// Actions here...
}

当然,您已在 中注册了您的服务 :Startup.cs

public void ConfigureServices(IServiceCollection services)  
{
services.AddScoped<IItemRepository, ItemRepository>();
services.AddScoped<IItemService, ItemService>();
}

效果很好,对吧?您的代码是干净的,服务被整齐地注入到控制器中或您需要的任何位置。但是,你有没有想过,当你像这样设置依赖关系注入 (DI) 时,幕后实际上发生了什么?框架如何知道要注入什么以及何时注入?

使用 DotNet-FullStack-Dev 踏上持续学习和探索的旅程。通过访问我们的 https://dotnet-fullstack-dev.blogspot.com 了解更多信息**。**

让我们深入研究一下 DI 在运行时会发生什么,以及它与 、 和 的关系。我们将抽离各个层次,以对话式、代码驱动的方式了解 .NET Core 如何为你处理所有这些魔力。Item APIItemServiceItemRepository

第 1 步:DI 容器准备就绪(启动阶段)

启动 .NET Core 应用时,首先要做的一件事是创建依赖项注入容器。把它想象成一个大盒子,你所有的服务注册(如 和 )都存储在其中。ItemServiceItemRepository

在 中,该方法注册具有特定**生命周期(**如 、 或 )的服务。框架现在知道需要哪些类以及它们应该存在多长时间:Startup.csConfigureServicesScopedTransientSingleton

public void ConfigureServices(IServiceCollection services)  
{
services.AddScoped<IItemRepository, ItemRepository>();
services.AddScoped<IItemService, ItemService>();
}

以下是此注册在人类术语中的含义:

  • AddScoped: “亲爱的 DI 容器,每次有 HTTP 请求时,请创建一个 and 实例,并在该请求中共享它。但不要留着下次请求。ItemServiceItemRepository

第 2 步:控制器请求依赖项

现在,假设您的应用程序收到了对 .框架看到你的控制器类在其构造函数中需要一个 of 实例:ItemsControllerIItemService

public class ItemsController : ControllerBase  
{
private readonly IItemService _itemService;

public ItemsController(IItemService itemService)
{
_itemService = itemService;
}

// Actions here...
}

这就是 DI 容器发挥作用的地方。容器看着你说,“好的,我需要给这个控制器一个 .我在哪里可以买到它?ItemsControllerIItemService

由于我们在 中注册,容器知道它应该提供 whenever 的实例。IItemServiceStartup.csItemServiceIItemService

第 3 步:解决依赖关系(连锁反应)

DI 容器不仅仅以 .真正的魔力发生在自身具有依赖项时,例如:IItemServiceItemServiceIItemRepository

public class ItemService : IItemService  
{
private readonly IItemRepository _itemRepository;

public ItemService(IItemRepository itemRepository)
{
_itemRepository = itemRepository;
}

// Service logic here...
}

由于 needs ,容器也会寻找如何解决该依赖关系。它看到 in 的注册,并提供 的实例 to 。ItemServiceIItemRepositoryIItemRepositoryStartup.csItemRepositoryItemService

这种连锁反应一直持续到所有依赖项都得到解决。在这种情况下:

  1. ItemsController 需要 ,因此容器提供了 .IItemServiceItemService

  2. ItemService 需要 ,因此容器提供了 .IItemRepositoryItemRepository

所有这些都是自动发生的 — 无需手动实例化,也不需要关键字。new

第 4 步:对象创建(幕后)

当容器解析所有依赖项时,它会根据需要实例化对象。让我们看看这个例子中容器在幕后做了什么:

ItemRepository 首先创建,因为依赖于它。ItemService

var itemRepository = new ItemRepository();

接下来创建 ItemService,并将其传递到其构造函数中。itemRepository

var itemService = new ItemService(itemRepository);

最后,创建 ItemsController 并注入其中。itemService

var itemsController = new ItemsController(itemService);

每个服务仅在需要时创建,并且每个 HTTP 请求仅创建一次(感谢)。AddScoped

第 5 步:请求后进行清理

HTTP 请求完成后,DI 容器将释放范围服务。此清理过程可确保正确释放任何资源(如数据库连接),从而防止内存泄漏。

对于 ,这意味着 和 的实例在请求结束后被释放,并为下一个请求创建新实例。AddScopedItemServiceItemRepository

你有没有像我一样至少问过自己一次,dependency-injection-is-a-curse-to-developers,然后看看我的想法。

为什么这一切对您作为开发人员来说都很重要

现在,你已了解了 .NET Core 中 DI 的幕后情况,你可以看到框架为你做了多少繁重的工作。这就是为什么这很重要:

  1. 无需手动创建对象:您不必担心实例化或 .DI 容器会处理这个问题。ItemServiceItemRepository

  2. 松散耦合:通过接口 (, ) 注入依赖项,您的代码更灵活,更易于测试。您可以在不更改其余代码的情况下交换实现。IItemServiceIItemRepository

  3. 自动生命周期管理:服务在正确的时间创建和销毁,这意味着您不必担心手动管理其生命周期。

  4. 可测试性:您可以在单元测试中模拟依赖项。例如,在 testing 时,您可以模拟以仅测试控制器的行为。ItemsControllerIItemService

快速了解依赖关系注入的实际应用

总而言之,我们来看一个典型的流程,当用户发出请求以从您的 .ItemsController

  1. Request comes in:用户点击终端节点。/api/items

  2. DI 容器启动:容器发现需要一个 .ItemsControllerIItemService

  3. 链式反应:容器通过创建 .它还会解析为 。IItemServiceItemServiceIItemRepositoryItemService

  4. 响应发出:控制器使用该服务从存储库中获取数据,并将响应发送回用户。

所有这些都无需手动实例化对象即可完成。您专注于编写干净的模块化代码,其余的由框架处理。

.NET Core 中的依赖项注入不仅仅是一种模式,它还是一种内置机制,可简化应用程序的结构并促进良好的软件设计。通过了解幕后发生的事情,您可以了解 DI 容器在运行时为您做了多少工作。

下次您注册服务或注入依赖项时,请考虑一下后台发生的无形连锁反应。DI 容器正在解决依赖关系、管理生命周期并确保一切顺利进行 — 同时您可以编写更简洁、更易于维护的代码。

如果你喜欢我的文章,请给我一个赞!谢谢

架构师老卢
资深软件架构师, 分享编程、软件设计经验, 教授前沿技术, 分享技术资源(每天发布电子书),每天进步一点点...
 最新文章