前言

踩了一个坑, 下面是 2 个 scope 的调用, 第 1 和 3 是一个 Audit log filter action, 第 2 个是 controller.

// open tran
// edit entity 1
// save change 1 // save point A
// edit entity 2
// save change 2 (fail, let's said due to sql unique constrain)
// catch { rollback to save point A } // edit entity 3
// save change 3 // commit (expected 1,3 will be saved)

需求是即使 controller 报错了, 但是 audit log 还是要记入. 直觉认为 controller 报错或 rollback to save point 就 ok 了,

但最终结果是 save change 3 也报错了. 因为 rollback to save point 并不会 rollback dbcontext state. 与至于 save change 3 耶跑了 edit entity 2, 这就报错了.

然后我就找到了这个 issue: DbContextTransaction should also rollback dbcontext, 提问者视乎也遇到了相同的情况.

参考:

Use both AddDbContextFactory() and AddDbContext() extension methods in the same project

Scoped service in DBContext with DBContextFactory can't resolve from root provider

DbContextTransaction should also rollback dbcontext

DbContext Lifetime, Configuration, and Initialization

DbContext = Unit of Work

在 EF Core Team 的回复中, 他们的角度是. 如果想要有一个 unit of work, 就开 1 个 dbcontext.

在这个 dbcontext 里面, 它负责维护修修改改的 entity state, 当 save changes 调用时, 它把所有 state 转换成 SQL query.

如果 save changes 顺利执行, 那么所有的 state 会 clear 掉 (变成 unchanged), 然后你可以开始下一轮的 unit of work.

如果 save changes 失败, 那么所有的 state 会保留着, 你可以修修改改在重试.

绝大部分情况下, 1 个 http request 用一个 unit of work 就足够了, 也就是一个 dbcontext 来 handle 就很恰当了, 所以默认 dbcontext 在 DI 是 scoped level.

但某些情况下可能会想要多个 unit of work for 1 http request, 这也是合理的.

可以使用 DbContextFactory 来创建.

要注意 lifetime, 它默认是 Singleton, 如果 ApplicationDbContext 有注入 scoped service 的话, 这里要改成 scoped.

总结, dbcontext 就像一个环境, 管理 entity state, 在一个 http request 中, 可以创建多个相同的 dbcontext, 来执行多个 unit of work, 这取决于想怎样去管理.

Unit of Work != Transaction

dbcontext 可以开启 transaction, 但也可以复用 transaction. 意味着, 多个 unit of work 是可以共享 transaction 的.

dbcontext save changes 以后, unit of work 就算完成了. 后续它有没有真的写入数据库, 这个得开 transaction 是否 commit/rollback.

所以可以理解 dbcontext 和 transaction 的职责是分的很开的. 这也是为什么 transaction rollback, dbcontext 却没有 rollback, 因为它们本来就没有什么牵连.

dbcontext save changes 成功的话, state 立马就 clear 了. 但这时 transaction 要 commit/rollback 根本还没有决定. 它才开始进入这个 step 而已.

总结

dbcontext = unit of work

1 个 http request 可以有多个 dbcontext 管理 entity state (比如 1 个 for audit log, 1 个 for controller, 它们互相不影响)

在 save changes 失败或, entity state 依旧会保留着, 可以 retry 也可以通过 dbContext.ChangeTracker.Clear() 清空它, 它可以把 Added 变成 Detached.

1 transaction 可以 shared with multiple dbcontext.

transaction 接手 dbcontext save changes 后的工作. 它决定那么 save changes 是否要永久写入数据库. 或者回滚, 或者回滚某些 save changes (基于 save point)

EF Core – Unit of Work, DbContext, Transaction 概念解释的更多相关文章

  1. EF Core 2.0中Transaction事务会对DbContext底层创建和关闭数据库连接的行为有所影响

    数据库 我们先在SQL Server数据库中建立一个Book表: CREATE TABLE [dbo].[Book]( ,) NOT NULL, ) NULL, ) NULL, ) NULL, [Cr ...

  2. EF Core如何输出日志到Visual Studio的输出窗口

    我们在使用EF Core的时候,很多时候需要在Visual Studio的输出窗口中知道EF Core在后台生成的SQL语句是什么,这个需求可以通过自定义EF Core的ILoggerFactory和 ...

  3. Asp.Net Core中创建多DbContext并迁移到数据库

    在我们的项目中我们有时候需要在我们的项目中创建DbContext,而且这些DbContext之间有明显的界限,比如系统中两个DbContext一个是和整个数据库的权限相关的内容而另外一个DbConte ...

  4. Asp.net core 学习笔记 ( ef core transaction scope & change level )

    ef core 有 unit of work 的概念,当我们 save change 时会自动使用 transaction 确保更新的一致性. 隔离级别是默认的 read committed 不允许脏 ...

  5. EF Core利用Transaction对数据进行回滚保护

    What? 首先,说一下什么是EF Core中的Transaction Transaction允许以原子方式处理多个数据库操作,如果事务已提交,则所有操作都应用于数据库,如果事务回滚,则没有任何操作应 ...

  6. asp.net core 系列 22 EF(连接字符串,连接复原,DbContext)

    一.连接字符串 在上二篇中,ASP.NET Core 应用程序连接字符串是写死在ConfigureServices代码中,下面介绍通过配置来实现.连接字符串可以存储在 appsettings.json ...

  7. 第三节:EF Core上下文DbContext相关配置和生命周期

    一. 配置相关 1. 数据库连接字符串的写法 (1).账号密码:Server=localhost;Database=EFDB01;User ID=sa;Password=123456; (2).win ...

  8. EF Core 快速上手——创建应用的DbContext

    系列文章 EF Core 快速上手--EF Core 入门 EF Core 快速上手--EF Core的三种主要关系类型 本节导航 定义应用的DbContext 创建DbContext的一个实例 创建 ...

  9. EF Core中DbContext可以被Dispose多次

    我们知道,在EF Core中DbContext用完后要记得调用Dispose方法释放资源.但是其实DbContext可以多次调用Dispose方法,虽然只有第一次Dispose会起作用,但是DbCon ...

  10. EF Core 中DbContext不会跟踪聚合方法和Join方法返回的结果,及FromSql方法使用讲解

    EF Core中: 如果调用Queryable.Count等聚合方法,不会导致DbContext跟踪(track)任何实体. 此外调用Queryable.Join方法返回的匿名类型也不会被DbCont ...

随机推荐

  1. 各类配置文件(DNS, Firefox,Edge)

    DNS配置 腾讯DNS: 119.29.29.29 2402:4e00::  2402:4e00:1:: 阿里云: 223.5.5.5 223.6.6.6 2400:3200::1 2400:3200 ...

  2. 女朋友问我 LB 是谁?

    科普一下 LB(负载均衡)技术 我的编程导航网站:www.code-nav.cn 大家好,我是鱼皮. 周末在家写代码,无意中跟女朋友提了下 LB,还说 LB 好的呱呱叫. 她笑了笑,问我 LB 是谁? ...

  3. [oeasy]python0073_进制转化_eval_evaluate_衡量_oct_octal_八进制

    进制转化 回忆上次内容 上次了解的是 整型数字类变量 integer 前缀为i   ​   添加图片注释,不超过 140 字(可选)   整型变量 和 字符串变量 不同 整型变量 是 直接存储二进制形 ...

  4. VSCode最强插件推荐(持续更新)

    一.通用插件 Codelf 描述:变量命名神器 Bracket Pair Colorizer 描述:成对的彩色括号,让括号拥有独立的颜色,便于区分 Prettier - Code formatter ...

  5. Node.js 处理 File

    Node.js 处理 File fs 模块 常规使用 运用递归遍历目录树 创建文件和目录 读写文件 path 模块 对于 file 的理解,此处 fs 模块 Node.js 提供了处理文件系统的内置模 ...

  6. Vue 打包后自定义样式无法覆盖elementUI组件原有样式问题

    Vue 打包后自定义样式无法覆盖elementUI组件原有样式问题   by:授客 QQ:1033553122   开发环境   Win 10   node-v10.15.3-x64.msi 下载地址 ...

  7. 题解:P10781 【MX-J1-T1】『FLA - III』Spectral

    本题的主要思路就是数学. 首先,让我们先来打一个表. \(i\) \(1\) \(2\) \(3\) \(4\) \(\dots\) \(T_{i}\) \(k\) \(1.5k\) \(1.5k\) ...

  8. springsecurity使用:登录与校验

    首先是引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  9. 如何在Linux云服务器上通过Docker Compose部署安装Halo,搭建个人博客网站?

    目录 前置步骤 环境搭建 创建容器组 在系统任意位置创建一个文件夹 创建docker-compose.yaml 启动 Halo 服务 配置反向代理以及域名解析 Halo初始化页面. 更新新版本的hal ...

  10. Jmeter函数助手14-TestPlanName

    TestPlanName函数获取当前测试计划保存的文件名称.该函数没有参数,直接引用即可${__TestPlanName}.