0

0

EF Core在后台任务中怎么使用 EF Core后台服务(IHostedService)中的用法

月夜之吻

月夜之吻

发布时间:2025-12-26 16:41:02

|

342人浏览过

|

来源于php中文网

原创

必须用 IServiceScope 显式创建新作用域来使用 DbContext,因后台服务长生命周期且 DbContext 非线程安全;需在 ExecuteAsync 中每次循环创建作用域、获取上下文、执行操作、及时释放,并响应取消令牌。

ef core在后台任务中怎么使用 ef core后台服务(ihostedservice)中的用法

在后台任务中使用 EF Core,核心是解决生命周期管理数据库上下文线程安全性问题。直接在 IHostedService(尤其是 BackgroundService)里复用 Web 请求作用域DbContext 会出错——因为后台服务是长生命周期,而默认注册的 DbContext 是 Scoped(每个请求一个实例),不能跨线程/跨作用域共享。

必须用 IServiceScope 显式创建新作用域

后台服务运行在独立线程,不自动拥有作用域。你需要从 IServiceProvider 创建一个新作用域,在其内获取 DbContext,用完立即释放。

  • 不要把 DbContext 声明为类字段(避免跨周期复用)
  • 每次执行任务逻辑前,用 serviceProvider.CreateScope() 开启新作用域
  • 从该作用域中解析 DbContext,执行查询或保存
  • 作用域对象(IServiceScope)用 using 确保及时释放,触发 DbContext 释放和连接归还

推荐继承 BackgroundService 而非直接实现 IHostedService

BackgroundService 封装了启动/停止逻辑和取消令牌传递,更安全易用。你的任务逻辑写在 ExecuteAsync(CancellationToken) 中。

  • 构造函数注入 IServiceProvider(不是 DbContext
  • ExecuteAsync 内部按需创建作用域和上下文
  • 务必监听传入的 CancellationToken,在取消时及时退出循环并释放资源
  • await Task.Delay(..., cancellationToken) 防止忙等

注意 DbContext 不是线程安全的

即使你在每次循环中都新建作用域和 DbContext,也不能在同一个 DbContext 实例上并发调用 SaveChangesAsync 或多个异步查询

AITDK
AITDK

免费AI SEO工具,SEO的AI生成器

下载
  • 一个 DbContext 实例只用于一次“单元工作”(比如查+改+保存)
  • 若需并行操作(如同时拉取多个 API 并分别存库),应为每个操作单独创建作用域和上下文
  • 避免在 async/await 链中跨 await 继续使用同一上下文(EF Core 6+ 对部分场景放宽,但不建议依赖)

示例:每 5 秒检查待处理订单

代码结构示意(省略异常处理和日志):

public class OrderProcessingService : BackgroundService
{
    private readonly IServiceProvider _serviceProvider;

    public OrderProcessingService(IServiceProvider serviceProvider)
        => _serviceProvider = serviceProvider;

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            try
            {
                using var scope = _serviceProvider.CreateScope();
                var context = scope.ServiceProvider.GetRequiredService();

                var pendingOrders = await context.Orders
                    .Where(o => o.Status == OrderStatus.Pending)
                    .ToListAsync(stoppingToken);

                foreach (var order in pendingOrders)
                {
                    order.Status = OrderStatus.Processing;
                    // ... 其他业务逻辑
                }

                await context.SaveChangesAsync(stoppingToken);
            }
            catch (OperationCanceledException)
            {
                break; // 正常退出
            }
            catch (Exception ex)
            {
                // 记录日志,但不要 throw,否则 BackgroundService 会终止
            }

            await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
        }
    }
}

注册方式(Program.cs):

services.AddHostedService();

基本上就这些。关键就是:不共享上下文、每次用都新开作用域、及时释放、响应取消信号。

相关专题

更多
线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

463

2023.08.10

数据库三范式
数据库三范式

数据库三范式是一种设计规范,用于规范化关系型数据库中的数据结构,它通过消除冗余数据、提高数据库性能和数据一致性,提供了一种有效的数据库设计方法。本专题提供数据库三范式相关的文章、下载和课程。

326

2023.06.29

如何删除数据库
如何删除数据库

删除数据库是指在MySQL中完全移除一个数据库及其所包含的所有数据和结构,作用包括:1、释放存储空间;2、确保数据的安全性;3、提高数据库的整体性能,加速查询和操作的执行速度。尽管删除数据库具有一些好处,但在执行任何删除操作之前,务必谨慎操作,并备份重要的数据。删除数据库将永久性地删除所有相关数据和结构,无法回滚。

2066

2023.08.14

vb怎么连接数据库
vb怎么连接数据库

在VB中,连接数据库通常使用ADO(ActiveX 数据对象)或 DAO(Data Access Objects)这两个技术来实现:1、引入ADO库;2、创建ADO连接对象;3、配置连接字符串;4、打开连接;5、执行SQL语句;6、处理查询结果;7、关闭连接即可。

346

2023.08.31

MySQL恢复数据库
MySQL恢复数据库

MySQL恢复数据库的方法有使用物理备份恢复、使用逻辑备份恢复、使用二进制日志恢复和使用数据库复制进行恢复等。本专题为大家提供MySQL数据库相关的文章、下载、课程内容,供大家免费下载体验。

250

2023.09.05

vb中怎么连接access数据库
vb中怎么连接access数据库

vb中连接access数据库的步骤包括引用必要的命名空间、创建连接字符串、创建连接对象、打开连接、执行SQL语句和关闭连接。本专题为大家提供连接access数据库相关的文章、下载、课程内容,供大家免费下载体验。

316

2023.10.09

数据库对象名无效怎么解决
数据库对象名无效怎么解决

数据库对象名无效解决办法:1、检查使用的对象名是否正确,确保没有拼写错误;2、检查数据库中是否已存在具有相同名称的对象,如果是,请更改对象名为一个不同的名称,然后重新创建;3、确保在连接数据库时使用了正确的用户名、密码和数据库名称;4、尝试重启数据库服务,然后再次尝试创建或使用对象;5、尝试更新驱动程序,然后再次尝试创建或使用对象。

401

2023.10.16

vb连接access数据库的方法
vb连接access数据库的方法

vb连接access数据库方法:1、使用ADO连接,首先导入System.Data.OleDb模块,然后定义一个连接字符串,接着创建一个OleDbConnection对象并使用Open() 方法打开连接;2、使用DAO连接,首先导入 Microsoft.Jet.OLEDB模块,然后定义一个连接字符串,接着创建一个JetConnection对象并使用Open()方法打开连接即可。

367

2023.10.16

虚拟号码教程汇总
虚拟号码教程汇总

本专题整合了虚拟号码接收验证码相关教程,阅读下面的文章了解更多详细操作。

25

2025.12.25

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Go 教程
Go 教程

共32课时 | 3万人学习

Go语言实战之 GraphQL
Go语言实战之 GraphQL

共10课时 | 0.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号