惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Simon Willison's Weblog
Simon Willison's Weblog
T
Threatpost
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Spread Privacy
Spread Privacy
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Engineering at Meta
Engineering at Meta
T
Tenable Blog
C
Cisco Blogs
T
The Blog of Author Tim Ferriss
NISL@THU
NISL@THU
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
P
Privacy & Cybersecurity Law Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
Secure Thoughts
N
News and Events Feed by Topic
Google DeepMind News
Google DeepMind News
博客园 - 三生石上(FineUI控件)
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
H
Hacker News: Front Page
I
InfoQ
L
LangChain Blog
Security Latest
Security Latest
The Cloudflare Blog
Forbes - Security
Forbes - Security
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Stack Overflow Blog
Stack Overflow Blog
TaoSecurity Blog
TaoSecurity Blog
量子位
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
Visual Studio Blog
Scott Helme
Scott Helme
爱范儿
爱范儿
A
Arctic Wolf
F
Full Disclosure
酷 壳 – CoolShell
酷 壳 – CoolShell
Schneier on Security
Schneier on Security
N
News and Events Feed by Topic
C
CXSECURITY Database RSS Feed - CXSecurity.com
L
LINUX DO - 最新话题
V2EX - 技术
V2EX - 技术
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
人人都是产品经理
人人都是产品经理
The Hacker News
The Hacker News
Hugging Face - Blog
Hugging Face - Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
Know Your Adversary
Know Your Adversary
Application and Cybersecurity Blog
Application and Cybersecurity Blog

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 分享一下笔者的 Mac 装机必备软件 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
在后台服务中使用 Scoped 服务,为什么总是报错?
yuyuyui · 2026-04-17 · via 博客园_首页

1.前言

在使用framwork时构造对象都是直接new,本身框架没有提供DI的能力,当然也可以集成Autofac ,因为Autofac 提供了完善的适配器,可以无缝集成到各类项目中,可以看出微软选择不是“修补旧船”,而是“建造新航母”,随着 .netcore 的发展,依赖注入也是被作为.netcore 平台框架的基础核心功能之一。

带来这个有好处最大的亮点就是依赖倒置,容器帮你管理对象的生命周期,在搭建框架时配置下,你完全不需要操心,只需要在构造函数写入就行了。

internal class SingletonService : ISingletonService
{
    public ITransientService Transient { get; }
    public Guid SingletonId { get; } = Guid.NewGuid();

    public SingletonService(ITransientService transient)
    {
        Transient = transient;
    }
}

相信有大部分小伙伴也是实际应用过的,只要按照项目内人家写的代码风格一样,往构造函数里放就完事了,其他的完全不用操心,框架也是尽可能这么做的,让你省心,但是您是否仔细思考一下您真的对他了解吗?因为新的技术意味着带来新的挑战,要学习新的东西,不然的话

1.假设哪天启动时突然构造函数不能注入了搞半天不知道怎么回事!

2.人家问你单例服务注入瞬时会不会报错,如果你没了解过,或者实际操作过的话,只能靠懵,我就遇到过,不过我不是靠懵,我是斩钉截铁的说,会!,因为慢一秒都代表心虚,底气来源就是,他的枪里没有子弹,(赌他可能也不知道)

接下来就聊聊一些需要注意的点,如果您知道,当看个热闹,如果不知道就当做扩展知识了

2. 三种服务生命周期的定义与行为

Transient(瞬时):每次从容器请求时都创建新实例,就跟每次直接new一样。

Scoped(作用域):在同一个 IServiceScope 内是单例,不同作用域之间实例不同,一个 Scope 对应一个逻辑上下文(如一次 HTTP 请求、一次后台任务)

同一作用域内:Scoped:服务是单例。Transient:服务每次解析都是新实例,但可共享同一个 Scoped 实例,就是你有2个不同的Transient引用了一个scope的类,在同一作用域2次解析的scope类都会是同一对象 。Singleton:整个应用程序生命周期内只有一个实例,就例如你定义的静态类。

  var services = new ServiceCollection();
  // 注入瞬时
  services.AddTransient<ITransientService, TransientService>();
  //注入单例
  services.AddSingleton<ISingletonService, SingletonService>();
  //注入作用域
  services.AddSingleton<IScopedService, ScopedService>();
  //构造服务对象
  var serviceProvider = services.BuildServiceProvider();

3.生命周期依赖的规则

这里说的就是,你不同生命周期的类,如果要相互引用需要参照并且按照以下的规则,依赖方的生命周期不能长于其依赖的服务,就是说你使用的类如果依赖其他类,那么一定不能存在当前类还存在,依赖的服务已经死了的情况。

短生命周期 → 依赖 → 长生命周期(Transient 依赖 Singleton 就可以)

长生命周期 → 依赖 → 短生命周期(Singleton 依赖 Scoped 或 Transient(有状态时),单例是不能依赖作用域的,依赖瞬时不会报错,但是瞬时推荐要无状态的类)

消费者类型 \ 依赖类型 Transient Scoped Singleton
Transient 允许 必须要在作用域内 允许
Scoped 允许 允许 允许
Singleton 需要注意(依赖不能长于自身) 不允许(依赖 Scoped) 允许

我们注意看这个表格中的关系,后面依赖这个展开,当然具体就是围绕3个特殊的来分析,因为容易出错

1.瞬时周期类依赖作用域周期类

2.单例周期类依赖瞬时类

3.单例周期类依赖作用域周期类

4.瞬时周期类依赖作用域周期类

控制台或者后台服务应用

1.根容器(IServiceProvider)不能解析 Scoped 服务,provider根容器,来获取注册为作用域周期的类就直接报错了

2.不能直接注入注册为作用域周期的类,例如我在瞬时的对象中,注入一个scoped周期的类

 internal class TransientService2 : ITransientService
 {
     public Guid Id { get; } = Guid.NewGuid();
     public IScopedService Scoped { get; }

     public TransientService2(IScopedService scoped)
     {
         Scoped = scoped;
         Console.WriteLine($"[Transient] 构造,ID: {Id},持有 Scoped: {scoped.Id}");
     }
 }

他就会出现如下错误

image

WebApi 应用

如果你善于观察,你会发现一个有趣的现象,这样的代码,如果放在api的控制器中注入
image

我这里分别注入依赖了作用域的瞬时,直接注入作用域,注入根容器,通过根容器获取作用域服务,他竟然全都不会报错。

结论

1.在控制台或者后台服务报错的原因,是因为作用域服务不能在根容器解析,这个如何理解呢?

因为作用域服务的生命周期是绑定在逻辑作用域(如一个 HTTP 请求)上的,每个逻辑操作单元一个实例,实例应该在该作用域内创建、共享、并在作用域结束时被释放。

而根容器(Root ServiceProvider)是应用程序生命周期级别的,它没有作用域的逻辑概念,如果直接从根容器解析作用域服务的话,就会:

1.作用域服务变成“伪单例”(在整个应用程序生命周期内复用,而不是每次请求一个新实例或作用域内共享)如果是DbContext ,那么同一个 DbContext 实例被多个 HTTP 请求、多个线程共享,虽然数据库连接本身可能被复用,但 DbContext 持有连接状态,长时间不释放 → 连接无法归还池 → 新请求无法获取连接 → 超时

2.因为没有真正的作用域上下文,破坏作用域的生命周期管理,不能正确触发作用域服务的释放和 Dispose(),可能造成内存泄漏或数据混乱。

那如何解决这个问题呢?答案就是我们需要通过作用域解析 Scoped 服务

var services = new ServiceCollection();
services.AddScoped<IScopedService, ScopedService>();
services.AddTransient<ITransientService, TransientService2>();

var provider = services.BuildServiceProvider(validateScopes: true);

// 通过作用域解析 Scoped 服务和依赖了 Scoped 的 Transient 服务
using (var scopeed = provider.CreateScope())
{
    var scopedProvider = scopeed.ServiceProvider;
    var scopedService = scopedProvider.GetRequiredService<ITransientService>();
}

可以发现scopedProviderprovider 都是 IServiceProvider,但它们代表的是两个不同层级的 DI 容器实例,生命周期和解析行为完全不同。根IServiceProvider 是整个应用程序级别的。而scopedProvider 是当前using块范围内
区分他们很简单,IServiceProvider 有一个IsScope属性,如果细心调试下会发现,通过scopeed 创建的IServiceProviderIsScopetrue,根容器IServiceProviderIsScopefalse

image

所以得出结论如果在后台服务线程或者控制台中要解析作用域周期对象,或者解析依赖了作用域周期的对象必须使用scopeed 来创建一个根容器来获取。

如果是在web中就不用管,因为微软已经在web框架中的RequestServicesFeature帮我们实现了将IServiceProvider 设为请求级别的 Scoped 根容器,在注入时可以发现它的IsScopetrue

image

可以看到每一次请求创建一个新的作用域

_scope = _scopeFactory.CreateScope();
_requestServices = _scope.ServiceProvider;  // 这是请求级别的容器

image

请求的生命周期执行过程

1.在管道的最开始(或通过 HttpContextFactory),会为这个请求创建一个 HttpContext对象

2.在这个过程中创建一个 请求级别的 IServiceProvider,然后将这个 IServiceProvider赋值给 HttpContext.RequestServices

3.后续不管是中间件、Controller、Page ,可以通过 HttpContext.RequestServices获取到当前请求的 Scoped 服务

注意点

如果是在控制器或者他引用的类中开启了后台线程,尽可能在主线程先获取子线程需要的数据传入,如果不可避免在后台中解析服务,依然还是要用根容器 来创建一个Scoped的根容器来获取

[HttpGet("run-task")]
public IActionResult RunBackgroundTask()
{
    // 只读取你需要的数据,而不是拿服务对象var data = "一些业务数据";
    Task.Run(() =>
    {
        // 这里不使用任何注入的服务
        Console.WriteLine($"Running in background with data: {data}");
    });
    
    // 方法2
    using (var scope = _serviceProvider.CreateScope())
    {
        var scopedService = scope.ServiceProvider.GetRequiredService<ScopedService>();
        scopedService.DoSomething();
    } 

    return Ok("OK");
}
场景 根容器 (IServiceProvider) 如何获取 Scoped 服务
Web API 本身就是 Scoped (IsScope=true) 直接注入即可
后台服务/控制台 不是 Scoped (IsScope=false) 必须 provider.CreateScope()

最后再聊聊开头那个面问题。为啥 Singleton 依赖 Scoped 会报错?说白了,就是因为根容器(Root)本身就是个 Singleton 级别的“大管家”,它根本没法直接给你变出一个有“时效性”的 Scoped 对象来。微软搞这套机制,初衷其实很简单:就是让咱们用的时候拿,不用了自动扔。在 Web 里,这个用的时候就是一次请求;在后台服务里,就是咱们手动 using 包裹的那一下。
所以,别管是写 Web 还是写后台,只要死记硬背这一条——根容器不能直接生 Scoped,你就彻底稳了。