在软件开发中,依赖注入已成为构建可维护、可测试和可扩展应用程序的核心模式。ASP.NET Core 内置的依赖注入容器为我们管理服务生命周期提供了极大的便利。然而在某些特定场景下,我们可能不希望某个依赖项在宿主对象被创建时立即实例化,而是希望它在首次被使用时才进行实例化。这就是“延迟注入”(Lazy Injection)的概念。

一、延迟注入的原理

延迟注入的核心思想是推迟对象的创建和初始化,直到真正需要使用它的时候。这在以下场景中尤为有用:

  1. 性能优化:当某个依赖项的创建成本很高(例如,需要进行复杂的计算、数据库查询或网络请求),但并非每次宿主对象被使用时都需要该依赖项时,延迟注入可以避免不必要的资源消耗,从而提升应用程序的启动速度和整体性能。
  2. 资源节约:如果某个依赖项会占用大量内存或其他系统资源,并且在应用程序的生命周期中可能只被少数几次使用,那么延迟注入可以帮助我们更有效地管理和节约资源。
  3. 解决循环依赖:在某些复杂的依赖关系图中,可能会出现循环依赖的情况。虽然良好的设计应该避免循环依赖,但在某些不可避免的场景下,延迟注入可以作为一种解决方案,打破循环,允许应用程序正常启动。

在 .NET 中,实现延迟注入最常用的方式是使用 System.Lazy<T> 类。Lazy<T> 是一个泛型类,它包装了一个对象,并确保该对象只在首次访问其 Value 属性时才被创建。Lazy<T> 的构造函数接受一个 Func<T> 委托,这个委托包含了创建被延迟加载对象的逻辑。当 Value 属性首次被访问时,这个委托会被执行,并将其结果缓存起来,后续的访问将直接返回缓存的值。

ASP.NET Core 的内置依赖注入容器本身并不直接支持 Lazy<T> 的自动解析。这意味着你不能直接在构造函数中注入 Lazy<TService>,并期望 DI 容器能够自动为你提供一个 Lazy<TService> 实例。然而,我们可以通过一些简单的配置和模式来实现延迟注入,尤其是在 .NET 8 环境下,其 DI 容器的性能和功能都有所增强,使得这些模式更加高效。

二、在 ASP.NET Core 中实现延迟注入

虽然 ASP.NET Core 的内置 DI 容器不直接支持 Lazy<T> 的自动解析,但我们可以利用其提供的 IServiceProvider 或通过注册工厂方法来实现延迟注入。以下将通过一个具体的代码示例来演示如何在 .NET 8 的 ASP.NET Core 应用程序中实现延迟注入。

假设我们有一个 ExpensiveService,它的创建成本很高,我们希望只在需要时才实例化它:

public interface IExpensiveService
{string GetData();
}public class ExpensiveService : IExpensiveService
{public ExpensiveService(){// 模拟耗时操作Console.WriteLine("ExpensiveService 实例被创建了!");System.Threading.Thread.Sleep(2000); }public string GetData(){return "这是来自 ExpensiveService 的数据。";}
}
2.1 方法一:通过 IServiceProvider 延迟解析

这是最直接的方法。你可以在需要延迟注入的类中注入 IServiceProvider,然后在需要时手动从 IServiceProvider 中解析服务。虽然这在一定程度上引入了服务定位器模式的痕迹,但在某些场景下是可接受的。

首先,在 Program.cs 中注册 ExpensiveService

// Program.csusing Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;var builder = WebApplication.CreateBuilder(args);// 注册 ExpensiveService
builder.Services.AddTransient<IExpensiveService, ExpensiveService>();// 添加控制器支持
builder.Services.AddControllers();var app = builder.Build();// 配置 HTTP 请求管道
if (app.Environment.IsDevelopment())
{app.UseDeveloperExceptionPage();
}app.UseRouting();app.UseEndpoints(endpoints =>
{endpoints.MapControllers();
});app.Run();

然后,在一个控制器中,你可以这样使用 IServiceProvider 来延迟解析 IExpensiveService

using Microsoft.AspNetCore.Mvc;
using System;[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{private readonly IServiceProvider _serviceProvider;public HomeController(IServiceProvider serviceProvider){_serviceProvider = serviceProvider;}[HttpGet("lazy-resolve")]public IActionResult LazyResolve(){Console.WriteLine("进入 LazyResolve 方法");// 只有在需要时才解析 ExpensiveServicevar expensiveService = _serviceProvider.GetService<IExpensiveService>();if (expensiveService != null){var data = expensiveService.GetData();return Ok($"延迟解析成功:{data}");}return NotFound("服务未找到。");}[HttpGet("no-lazy-resolve")]public IActionResult NoLazyResolve(){Console.WriteLine("进入 NoLazyResolve 方法");// 不进行延迟解析,如果 ExpensiveService 在构造函数中被注入,则会立即创建return Ok("未进行延迟解析。");}
}

当你访问 /home/lazy-resolve 时,ExpensiveService 的构造函数只会在 GetService<IExpensiveService>() 被调用时才执行。而访问 /home/no-lazy-resolve 则不会触发 ExpensiveService 的实例化(除非 ExpensiveService 被其他非延迟注入的方式所依赖)。

2.2 方法二:注册 Lazy<T> 的工厂方法

这种方法更加优雅,它允许你直接在构造函数中注入 Lazy<TService>,而无需直接暴露 IServiceProvider。你需要手动注册一个工厂方法,告诉 DI 容器如何创建 Lazy<TService> 的实例。

Program.cs 中,你可以这样注册 Lazy<IExpensiveService>

// Program.cs (部分代码)// ...builder.Services.AddTransient<IExpensiveService, ExpensiveService>();// 注册 Lazy<IExpensiveService>
builder.Services.AddTransient(sp => new Lazy<IExpensiveService>(() => sp.GetRequiredService<IExpensiveService>()));// ...

现在,我们可以在控制器中直接注入 Lazy<IExpensiveService>

using Microsoft.AspNetCore.Mvc;
using System;[ApiController]
[Route("[controller]")]
public class LazyInjectionController : ControllerBase
{private readonly Lazy<IExpensiveService> _lazyExpensiveService;public LazyInjectionController(Lazy<IExpensiveService> lazyExpensiveService){_lazyExpensiveService = lazyExpensiveService;Console.WriteLine("LazyInjectionController 构造函数执行,但 ExpensiveService 尚未创建。");}[HttpGet("lazy-injection")]public IActionResult LazyInjection(){Console.WriteLine("进入 LazyInjection 方法");// 首次访问 .Value 属性时,ExpensiveService 才会被创建var data = _lazyExpensiveService.Value.GetData();return Ok($"延迟注入成功:{data}");}[HttpGet("no-lazy-injection")]public IActionResult NoLazyInjection(){Console.WriteLine("进入 NoLazyInjection 方法");return Ok("未访问延迟注入的服务。");}
}

当你访问 /lazyinjection/lazy-injection 时,ExpensiveService 的构造函数只会在 _lazyExpensiveService.Value 被首次访问时才执行。而访问 /lazyinjection/no-lazy-injection 则不会触发 ExpensiveService 的实例化。

2.3 方法三:使用第三方 DI 容器

一些第三方依赖注入容器,如 Autofac,对 Lazy<T> 有原生支持,使得延迟注入的实现更加简洁。如果你已经在项目中使用或计划使用第三方 DI 容器,这可能是一个更方便的选择。

以 Autofac 为例,你通常只需要注册你的服务,Autofac 会自动处理 Lazy<T> 的解析:

// Startup.cs (Autofac 配置示例)public class Startup
{public ILifetimeScope AutofacContainer { get; private set; }public void ConfigureServices(IServiceCollection services){services.AddControllers();}public void ConfigureContainer(ContainerBuilder builder){builder.RegisterType<ExpensiveService>().As<IExpensiveService>().InstancePerDependency();}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){AutofacContainer = app.ApplicationServices.GetAutofacRoot();if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseRouting();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});}
}

然后,在控制器中直接注入 Lazy<IExpensiveService> 即可:

using Microsoft.AspNetCore.Mvc;
using System;[ApiController]
[Route("[controller]")]
public class AutofacLazyInjectionController : ControllerBase
{private readonly Lazy<IExpensiveService> _lazyExpensiveService;public AutofacLazyInjectionController(Lazy<IExpensiveService> lazyExpensiveService){_lazyExpensiveService = lazyExpensiveService;Console.WriteLine("AutofacLazyInjectionController 构造函数执行,但 ExpensiveService 尚未创建。");}[HttpGet("autofac-lazy-injection")]public IActionResult AutofacLazyInjection(){Console.WriteLine("进入 AutofacLazyInjection 方法");var data = _lazyExpensiveService.Value.GetData();return Ok($"Autofac 延迟注入成功:{data}");}
}

TIP:使用 Autofac 需要额外的配置步骤来集成到 ASP.NET Core 中,这里仅展示了核心的延迟注入部分。

三、总结

延迟注入是 ASP.NET Core 中一种重要的性能优化和资源管理策略,尤其适用于那些创建成本高昂或不总是需要的服务。通过 System.Lazy<T> 类,我们可以有效地推迟对象的实例化,直到它们真正被使用。虽然 ASP.NET Core 的内置 DI 容器不直接支持 Lazy<T> 的自动解析,但我们可以通过注册工厂方法或注入 IServiceProvider 来实现这一目标。对于更复杂的场景,或者如果你已经在使用第三方 DI 容器,它们通常会提供更简洁的 Lazy<T> 解析支持。在 .NET 8 及其后续版本中,随着框架性能的不断提升,合理地运用延迟注入将有助于构建更高效、响应更迅速的应用程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/90929.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/90929.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/90929.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PHP内存溢出问题的深度分析与系统解决方案

文章目录一、问题本质&#xff1a;什么是PHP内存溢出&#xff1f;内存管理核心原理二、高频内存溢出场景深度解析场景1&#xff1a;大数据集不当处理场景2&#xff1a;无限递归陷阱场景3&#xff1a;实体关系映射&#xff08;ORM&#xff09;的N1问题场景4&#xff1a;未及时释…

常见 HTTP 方法的成功状态码200,204,202,201

HTTP 协议中&#xff0c;操作成功后的状态码选择取决于操作类型和响应内容&#xff0c;并非所有非 GET/POST 请求都返回 204。以下是常见 HTTP 方法的成功状态码规范&#xff1a;1. GET200 OK&#xff1a;默认成功状态码&#xff0c;表示请求成功且返回了资源内容。206 Partial…

【论文阅读】Think Only When You Need with Large Hybrid-Reasoning Models

Think Only When You Need with Large Hybrid-Reasoning Models2 Large Hybrid-Reasoning Models2.1 Problem Formulation关键定义与目标核心挑战与解决方案2.2 第一阶段&#xff1a;混合微调&#xff08;Hybrid Fine-Tuning, HFT&#xff09;核心设计数据构建数据集统计优化目…

洛谷 P13014:[GESP202506 五级] 最大公因数

【题目来源】 https://www.luogu.com.cn/problem/P13014 【题目描述】 对于两个正整数 &#xff0c;他们的最大公因数记为 。对于 个正整数 &#xff0c;他们的最大公因数为&#xff1a; 给定 个正整数 以及 组询问。对于第 组询问&#xff0c;请求出 的最大公因数&…

构建应用内智能:衡石嵌入式BI如何打造“指标中台”驱动的场景化分析

在当今数据驱动的业务环境中&#xff0c;将智能分析能力深度嵌入业务应用&#xff08;如CRM、ERP、SCM、自研SaaS&#xff09;已成为刚需。然而&#xff0c;实现高性能、一致性、可治理的嵌入式分析面临巨大技术挑战。衡石科技通过其核心的指标中台&#xff08;Metric Platform…

带货视频评论洞察 Baseline 学习笔记 (Datawhale Al夏令营)

一、 项目认识背景&#xff1a;电商直播/短视频已积累大量「视频 评论」数据&#xff0c;蕴含了消费者的真实反馈。目标&#xff1a;通过「商品识别 → 情感分析 → 评论聚类」三步&#xff0c;辅助品牌洞察、网红投放评估。二、 Baseline 代码流程1. 读取和预处理video_data …

uniapp中使用uView-plus踩坑记录

​​​1.使用插件市场安装点击到插件市场 零云uview-plus3.0重磅发布&#xff0c;全面的Vue3鸿蒙移动组件库。 - DCloud 插件市场 点击选择项目直接导入就可以&#xff0c;下载完成后会在uni_modules中&#xff0c;这个.gitignore中不可忽略 ​ 使用在main.js里引入 import…

openGauss数据库管理实战指南——基本常用操作总结

查看所有数据库 查看所有表 \d 查看函数定义 查看所有用户 select usename from pg_user; 1.数据库创建管理 CREATE DATABASE test; 2.数据库用户创建管理 CREATE USER tom PASSWORD Root123456.; 3.表的创建及管理 3.1.创建表 CREATE TABLE test(ID INTEGER PRIMARY …

智慧公安信息化建设解决方案PPT(63页)

智慧公安的定义与职能 智慧公安是利用现代信息技术提升公安工作效率与服务质量的新模式&#xff0c;涵盖刑事侦查、治安管理、交通管理等多方面职能&#xff0c;致力于保障社会安全与秩序。 智慧公安信息化建设的重要性 信息化建设是智慧公安发展的核心&#xff0c;通过数据…

k8s存储入门

目录 一、 Volume 的概念 二、 Volume 的类型 三、 通过 emptyDir 共享数据 1. EmptyDir 特性 2. EmptyDir 共享数据 四&#xff1a;使用 HostPath 挂载宿主机文件 1. HostPath 特性 2. 挂载宿主机时区文件 五、 挂载 NFS 至容器 1. 前置准备&#xff08;所有 K8s 节…

基于 Flutter 的开源文本 TTS 朗读器(支持 Windows/macOS/Android)

界面特性 基于 Flutter 的文本 TTS 朗读器支持 Windows、macOS、AndroidTTS 源&#xff1a;OpenAI TTS、Microsoft TTS支持设置代理支持设置应用主题支持倍速支持书签支持点击指定地方朗读支持 txt、epub、贴粘文本支持从上次地方开始朗读 源代码https://github.com/xchenhao/t…

深入理解大语言模型:从核心技术到极简实现

零基础的读者建议先看《零基础理解大语言模型&#xff1a;从生活例子到代码实现》&#xff0c;本教程的完整代码可以在GitHub上找到&#xff0c;如果你有任何问题或建议&#xff0c;欢迎交流讨论。 引言 自ChatGPT横空出世以来&#xff0c;大语言模型&#xff08;Large Langua…

7月13日日记

看来每天写一篇日记对我来说还是一个不小的挑战。主要是和惰性做抗争吧。但是这个东西说实话也没有什么难度&#xff0c;也并不占用时间&#xff0c;一篇日记大概十几分钟就可以写完。可能更多的是健忘。忘了每天有一个这样的小任务。忘了前几天日记写没写了&#xff0c;三下乡…

《Stata面板数据分析:数据检验、回归模型与诊断技术 - 以NLSW工资研究(公开数据)为例》

本教程旨在全面介绍使用 Stata 进行面板数据分析的方法和技巧。我们将以美国国家纵向调查(NLSW)的数据为例,系统地探讨从基础 OLS 回归到高级固定效应模型的分析过程。 NLSW 数据集是公开的,可以免费获取,这为读者提供了实践和复现的机会。 通过这个教程,您将掌握使用 …

【VSCode+LaTeX】科研写作环境搭建

文章目录0 引言为什么选择LaTeXVSCode&#xff1f;为什么不选择Overleaf&#xff1f;1 TeXLive安装1.1 下载安装包1.2 运行安装程序1.3 通过镜像安装2 VSCode安装与配置2.1 下载VSCode安装包2.2 安装VSCode2.3 安装中文语言包2.4 配置LaTeX核心扩展2.5 加载TeX模版文件2.6 编译…

Surfer软件入门与等值线绘制实操教程

本文还有配套的精品资源&#xff0c;点击获取 简介&#xff1a;本教程将指导初学者如何使用Surfer软件进行地质绘图&#xff0c;重点在于等值线的绘制技巧和提升图形质量。内容涵盖Surfer界面介绍、数据导入、等值线绘制方法、样式设置、地图增强技术以及输出保存方法&#…

攻防世界——Web题 very_easy_sql

目录 payload1 payload2 payload3 看到了题目是sql就猜测是sql注入和万能密码了&#xff0c;但怎么试貌似都没有反应&#xff0c;看源代码发现了use.php 访问use.php页面 可以猜测这里是SSRF&#xff0c;可以访问到我们本不能访问的界面&#xff0c;比如&#xff1a;服务器…

基于 SpringBoot 的 REST API 与 RPC 调用的统一封装

一、为何需要统一封装&#xff1f; 在讨论统一封装之前&#xff0c;我们先看看 REST 和 RPC 各自的适用场景。 REST API 基于 HTTP 协议&#xff0c;采用 JSON 作为数据交换格式&#xff0c;可读性好且跨语言&#xff0c;非常适合对外提供服务。 RPC&#xff08;如 Dubbo、gRPC…

【SpringBoot】 整合MyBatis+Postgresql

MyBatis 是一个轻量级的持久化框架&#xff0c;用于简化数据库访问和操作。它通过将 SQL 语句与 Java 代码分离&#xff0c;允许开发者使用 XML 或注解来配置 SQL 语句&#xff0c;并将结果映射为 Java 对象。MyBatis 提供了灵活的 SQL 控制&#xff0c;适合需要精细控制 SQL 的…

无缝衔接直播流体验

文章目录前言&#x1f9e0; 1. 为什么能“无缝衔接”&#xff1f;&#x1f9f0; 2. Flutter 实现方案✅ 总体策略&#x1f3af; 核心技术点✅ a. 使用全局播放器管理器&#xff08;单例模式&#xff09;✅ b. 广场页中的直播卡片使用播放器✅ c. 详情页复用控制器✅ d. 页面切换…