前言

踩了一个坑, 下面是 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. 直播预告:Service Mesh 技术在美团的落地和挑战

    一场突如其来的疫情加深了企业对数字化转型升级的渴望,作为新兴数字化业务的基础,云原生技术的价值日益凸显.当前,越来越多的企业逐步引入容器.微服务/Service Mesh 技术改造业务,实现数据库.P ...

  2. Profibus_DP转ModbusTCP网关模块接马保通讯案例

    某工业企业为了提高生产效率和管理水平,决定对其生产线进行智能化改造.在该项目中,利用巴图自动化Profibus_DP转ModbusTCP网关模块(BT-ETHPB20)连接了不同生产设备,实现了设备之 ...

  3. useHeadSafe:安全生成HTML头部元素

    title: useHeadSafe:安全生成HTML头部元素 date: 2024/7/17 updated: 2024/7/17 author: cmdragon excerpt: 摘要:&quo ...

  4. CF1204A 题解

    洛谷链接&CF 链接 本篇题解为此题较简单做法及较少码量,并且码风优良,请放心阅读. 题目简述 给定一个二进制字符串 \(S\),求这个二进制数包含 \(4 ^ k\) 的个数. 特殊的:若 ...

  5. Odoo 菜单定义和修改学习总结

    odoo菜单定义和修改学习总结 环境 odoo-14.0.post20221212.tar 定义菜单 方式1: <?xml version="1.0"?> <od ...

  6. Pytest Pytest源码分析

    Pytest源码分析 By:授客 QQ:1033553122 测试环境 pytest 5.4.3 测试脚本mytest.py #!/usr/bin/env python # -*- coding:ut ...

  7. Git 克隆仓库报unable to get local issuer certificate错误解决方法

    Git 克隆仓库报unable to get local issuer certificate错误解决方法 By:授客 QQ:1033553122 问题描述 克隆gitlab上的仓库,报错,如下 $ ...

  8. 数据结构:Deuque

    #include <iostream> #include <stdio.h> #include <string> using namespace std; stru ...

  9. python global变量作用域

    python变量作用域 var1 = 123 def func(): var1 = 456 print(var1) func() #456 print(var1) #123 ============= ...

  10. MapGIS路网数据发布

    准备 1.MapGIS 10 桌面版(我用的10.5.6.10) 2.路网的shp文件 数据导入 1.创建要素集,如果已有要素集可以不用创建: 2.导入路网要素类,选择准备好的shp文件后导入即可: ...