EF中关于TransactionScope的使用
前提条件
TransactionScope类需要引用System.Transactions;
数据库环境及需求
现在假设有两个表如图:
表TA 表TB
现有数据:
现在的需求是:每往TA中插入一条数据,就更新TB的第一行,值为表TA的所有行的Age的平均值
可以看到表TB的Remark长度仅仅为2,待会利用这个制造错误
当不使用TransactionScope时:

using (EFTestEntities db = new EFTestEntities()) { //数据库TA原有2行,此时添加第3行 var aEntity = new TA { Name = "a", Age = 20 }; db.TA.Add(aEntity); var listTA = db.TA.ToList();//此时list只有2行,新添加的没有读取到 int totalAge = 0; listTA.ForEach(p => totalAge += p.Age); //获取表TB的第一行,并修改AvgAge值 var bEntity = db.TB.First(); bEntity.AvgAge = totalAge / listTA.Count; bEntity.Remark = "bb"; db.SaveChanges(); //结果是Tb的值没有变化 }
你认为是添加TA后没有db.SaveChanges();? 的确,添加这个之后,可以读取到新添加的值了,但是没有事务了,如:

using (EFTestEntities db = new EFTestEntities()) { //数据库TA原有2行,此时添加第3行 var aEntity = new TA { Name = "a", Age = 20 }; db.TA.Add(aEntity); db.SaveChanges(); var listTA = db.TA.ToList();//此时list有3行了,新添加的可以被读取到 int totalAge = 0; listTA.ForEach(p => totalAge += p.Age); //获取表TB的第一行,并修改AvgAge值 var bEntity = db.TB.First(); bEntity.AvgAge = totalAge / listTA.Count; bEntity.Remark = "bbc";//故意超出长度,制造错误 db.SaveChanges(); //结果是TA添加了新行,但是TB修改时出错导致修改失败。造成了数据不一致 }
结果是TA添加了新行,但是TB因为remark超出长度导致导致修改失败,此时TB的数据是错的。
怎样可以避免这个问题?答案就是用事务。
使用TransactionScope:

using (EFTestEntities db = new EFTestEntities()) { using (TransactionScope ts = new TransactionScope()) { //数据库TA原有2行,此时添加第3行 var aEntity = new TA { Name = "a", Age = 20 }; db.TA.Add(aEntity); db.SaveChanges();//重点,必须要db.SaveChanges(),然后下面才能获取到新添加的行 var listTA = db.TA.ToList();//此时list有3行了,新添加的可以被读取到 int totalAge = 0; listTA.ForEach(p => totalAge += p.Age); //获取表TB的第一行,并修改AvgAge值 var bEntity = db.TB.First(); bEntity.AvgAge = totalAge / listTA.Count; bEntity.Remark = "bbc";//故意超出长度,制造错误 db.SaveChanges(); //执行到此处程序报错 ts.Complete();//事务提交未执行 //结果是自动回滚,相当于此次没有对数据库做任何操作。保持了数据一致性 } }
下面给出正确的示例,实现以上需求:

using (EFTestEntities db = new EFTestEntities()) { using (TransactionScope ts = new TransactionScope()) { //数据库TA原有2行,此时添加第3行 var aEntity = new TA { Name = "a", Age = 20 }; db.TA.Add(aEntity); db.SaveChanges();//重点,必须要db.SaveChanges(),然后下面才能获取到新添加的行 var listTA = db.TA.ToList();//此时list有3行了,新添加的可以被读取到 int totalAge = 0; listTA.ForEach(p => totalAge += p.Age); //获取表TB的第一行,并修改AvgAge值 var bEntity = db.TB.First(); bEntity.AvgAge = totalAge / listTA.Count; bEntity.Remark = "bb";//数据符合规范 db.SaveChanges(); //保存 ts.Complete();//提交事务 //结果是执行成功,TA多一条数据,同时TB的值也变了 } }
一切都nice了!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构