zoukankan      html  css  js  c++  java
  • EF Core – Unit of Work, DbContext, Transaction 概念解释

    前言

    踩了一个坑, 下面是 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)

  • 相关阅读:
    人工智能第一次作业
    在uni-app的textarea中输入纯数字或者英文不换行的问题
    uni-app实现选择图片上传并显示进度条
    减肥计划
    前端时间格式2020-02-11T12:24:18.000+0000转化成正常格式
    男孩和女孩
    java 获取当前年份 月份 日期
    深海收破烂
    如何爱一个人
    随手心情
  • 原文地址:https://www.cnblogs.com/keatkeat/p/15755100.html
Copyright © 2011-2022 走看看