写在前面:

1. 本文中单元测试用到的数据库,在执行测试之前,会被清空,即使用空数据库。

2. 本文中的单元测试都是正确通过的。

要理解EF的事务机制,首先要理解这2个类:TransactionScope和DbContext。

DbContext是我们的数据库,通常我们会建一个类MyProjectDbContext继承自DbContext,里面包含所有的数据库表。这个类相当于定义了一个完整的数据库。

下面通过一些单元测试来看看这2个类是如何工作的。

  1. [Test]
  2. public void Can_Rollback_On_Errors_In_Different_Context()
  3. {
  4. var user1 = Mock.Users.Random();
  5. var user2 = Mock.Users.Random();
  6. user2.FirstName = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
  7. var userCount = ;
  8. try
  9. {
  10. using (var scope = new TransactionScope())
  11. {
  12. using (var db = new MyProjectDbContext())
  13. {
  14. db.Users.Add(user1);
  15. db.SaveChanges();
  16. userCount = db.Users.Count();
  17. }
  18. using (var db = new MyProjectDbContext())
  19. {
  20. db.Users.Add(user2);
  21. db.SaveChanges();//will throw exception
  22. }
  23. scope.Complete();
  24. }
  25. }
  26. catch(Exception)
  27. {
  28.  
  29. }
  30. Assert.AreEqual(, userCount);
  31. using (var db = new MyProjectDbContext())
  32. {
  33. Assert.AreEqual(, db.Users.Count());
  34. }
  35. }

注意第一个assert,userCount是等于1的,也就是说第一个db.SaveChanges()是顺利执行了的。但是看看第二个assert,数据库里面却没有user记录。这就是使用TransactionScope得到的真正的事务机制。

再看一个测试:

  1. [Test]
  2. public void Cannot_Rollback_Without_Scope()
  3. {
  4. var user1 = Mock.Users.Random();
  5. var user2 = Mock.Users.Random();
  6. user2.FirstName = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
  7. var userCount = ;
  8. try
  9. {
  10. using (var db = new MyProjectDbContext())
  11. {
  12. db.Users.Add(user1);
  13. db.SaveChanges();
  14. userCount = db.Users.Count();
  15. }
  16. using (var db = new MyProjectDbContext())
  17. {
  18. db.Users.Add(user2);
  19. db.SaveChanges();//will throw exception
  20. }
  21. }
  22. catch (Exception)
  23. {
  24.  
  25. }
  26. Assert.AreEqual(, userCount);
  27. using (var db = new MyProjectDbContext())
  28. {
  29. Assert.AreEqual(, db.Users.Count());
  30. }
  31. }

这个测试跟上面的测试差不多,唯一的区别就是没有使用TransactionScope把两个DbContext包起来。于是每个DbContext成为独立的事务。

再来看一个测试:

  1. [Test]
  2. public void Shouldnot_SaveToDB_As_ScopeNotComitted()
  3. {
  4. var user1 = Mock.Users.Random();
  5. var userCount = ;
  6. try
  7. {
  8. using (var scope = new TransactionScope())
  9. {
  10. using (var db = new MyProjectDbContext())
  11. {
  12. db.Users.Add(user1);
  13. db.SaveChanges();
  14. userCount = db.Users.Count();
  15. }
  16. //scope.Complete();
  17. }
  18. }
  19. catch (Exception)
  20. {
  21.  
  22. }
  23. Assert.AreEqual(, userCount);
  24. using (var db = new MyProjectDbContext())
  25. {
  26. Assert.AreEqual(, db.Users.Count());
  27. }
  28. }

}

这个测试表明,一旦DbContext被TransactionScope包起来之后,那么scope必须要调用scope.Complete()才能将数据更新到数据库。

基于上面的这些知识,我们可以很容易为EF搭建支持真正事务的框架。下面我简单介绍下我们的项目架构(EF CodeFirst, MVC)。

基于EF事务机制的架构

Domain层:

定义数据实体类,即数据库中的表。定义继承自DbContext的MyProjectDbContext。

Service层:

主要用于封装所有对数据库的访问。例子代码如下:

  1. public List<User> GetAllUsers()
  2. {
  3. using (var db = new MyProjectDbContext())
  4. {
  5. return db.Users.ToList();
  6. }
  7. }

上面这段代码中注意要使用using,否则DbContext的延迟加载功能会在controller层被调用。加了using之后,可以避免在controller层对数据库的直接访问。

Controller层:

调用service层的代码从数据库中得到数据,返回给UI。例子:

  1. public ActionResult GetAllUsers()
  2. {
  3. var users = IoC.GetService<IUserService>().GetAll();
  4. return View(users);
  5. }

同时将UI传回来的数据更新到数据库,这时如果需要调用多个service来更新数据库,那么就需要用到事务。例子:

  1. public ActionResult DeleteUser(int userId)
  2. {
  3. try
  4. {
  5. using (var scope = new TransactionScope())
  6. {
  7. IoC.GetService<IUserService>().DeleteLogs(userId);
  8. IoC.GetService<IUserService>().DeleteUser(userId);
  9. scope.Complete();
  10. return View();
  11. }
  12. }
  13. catch(Exception)
  14. {
  15.  
  16. }
  17. return View();
  18. }

通常情况下,我们会在MyControllerBase里面加一个 ActionResult TryScope(Action action)的方法,这样在子类里面就可以不用写try-catch了。

对于EF更深层的机制,我了解的也不多。欢迎大家讨论!

分享我们项目中基于EF事务机制的架构的更多相关文章

  1. 分享我们项目中基于EF事务机制的架构 【转载】

    http://www.cnblogs.com/leotsai/p/how-to-use-entity-framework-transaction-scope.html 写在前面: 1. 本文中单元测试 ...

  2. [翻译 EF Core in Action 1.10] 应该在项目中使用EF Core吗?

    Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...

  3. 在项目中部署redis的读写分离架构(包含节点间认证口令)

    #### 在项目中部署redis的读写分离架构(包含节点间认证口令) ##### 1.配置过程 ---  1.此前就是已经将redis在系统中已经安装好了,redis utils目录下,有个redis ...

  4. 采用EntLib5.0(Unity+Interception+Caching)实现项目中可用的Caching机制

    看了园子里很多介绍Caching的文章,多数都只介绍基本机制,对于Cache更新和依赖部分,更是只简单的实现ICacheItemRefreshAction接口,这在实际项目中是远远不够的.实际项目中, ...

  5. visual studio 项目中使用EF创建的数据库,后续更新数据库操作(生产已经部署,不能删除数据库重新创建)

    情景:SharePoint项目(其他类型的项目道理也一样),数据库是用EF(版本:6.0.0.0)创建的,生产环境已经使用,所以后续修改数据库,只能通过更新来实现. 下面是具体的操作方式: 1.vis ...

  6. 关于项目中的DAL数据接入层架构设计

    摘要:项目中对关系型数据库的接入再寻常不过,也有海量的ORM工具可供选择,一个一般性的DAL数据接入层的结构却大同小异,这里就分享一下使用Hibernate.Spring.Hessian这三大工具对D ...

  7. 实际项目中遇到EF实体类的操作问题及操作方法

    之前一直做ASP,都是直接写数据库操作语句,但是现在使用linq或者EF了,具体数据库操作不会了,遇到几个问题,然后经过查找资料解决了,记录一下. 一.遇到序列化问题 遇到循环引用问题,我的项目是一个 ...

  8. 在Blazor Server 项目中使用 EF Core Sqlite

    按照教程创建了一个 Blazor Server 项目 教程地址: https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/build-a-blaz ...

  9. vue项目中基于D3.js实现桑基图功能

    前端实现数据可视化的方案有很多种,以前都是使用百度的echarts,使用起来很方便,直接按照特定的数据格式输入,就能实现相应的效果,虽然使用方便,但是缺点就是无法自定义一些事件操作,可自由发挥的功能很 ...

随机推荐

  1. Blender 之修改器代码分析

                           Blender的修改器(modifier)模块,默认界面右下块(Property)面板的扳手,分类(修改.生成.形变.模拟)列出所有的修改器.也可以空格键 ...

  2. Java基础学习(三)

    /* java中的八种基本数据类型: 整数: byte . short . int . long 小数: float double 字符: char 布尔: boolean 字符串的类型: Strin ...

  3. Datazen配置

    Datazen是被微软收购的移动端全平台的数据展现解决方案.此篇主要介绍其服务器端的配置过程. 在上一篇的基础安装完成之后,在浏览器敲入如下地址进入系统的控制面板(留意安装的时候配置的是80 web端 ...

  4. How GitHub Works《Github是如何工作的?》

    https://github.com/blog/920-how-github-works 如果你想知道Github是如何工作的,你可以看查看Zach Holman(@holman)的三篇文章: Hou ...

  5. UWP学习记录1-开端

    UWP学习记录1-开端 1.背景 针对不同基础的人,学习的路线自然是不同的.这篇文章记录的是我个人的学习路线,或者说笔记.我对自己的技术状态的定义是: A.有很好的windows平台编程基础: B.有 ...

  6. 关于ubuntu16无线网卡RTL8723BE频繁掉线及信号不足的解决办法

    最近在新电脑上装了ubuntu16,结果wifi经常连不上,连上了过段时间就掉线,路由器就在电脑的旁边,而且信号非常的若. 但是windows系统没有任何问题,所以就在网上找解决办法,也按照网上的方法 ...

  7. ubuntu14.04下配置Java环境以及安装最新版本的eclipse

    首先是配置JDK 步骤一:下载最新版本的JDK,链接:http://www.oracle.com/technetwork/java/javase/downloads/index.html 步骤二:首先 ...

  8. JS简单解决并发量

    经常在写代码的时候碰到这样的场景:页面初始化时显示loading页,同时启动多个ajax并发请求获取数据,当每个ajax请求返回时结束loading. 举个例子,一个下订单的页面,要查询常用地址信息. ...

  9. Git 简介

    版本控制 什么是版本控制? 我需要版本控制吗? - 如果你还没使用过版本控制系统,或许你会有以上疑问,甚至更多疑问.希望后面的回答能让你喜欢上版本控制系统,喜欢上Git. 什么是版本控制:顾名思义,版 ...

  10. 【MongoDB初识】-结合C#简单使用,驱动2.x

    public static Students GetEntityByName(string conStr, string userName = "bj") { Students s ...