分享我们项目中基于EF事务机制的架构

摘要:
16userCount=db.Users.Count();17} 18使用(vardb=newMyProjectDbContext())19{20db.Users.Add(user2);//willsoutexception22}23scope.Complete();14userCount=db.Users.Count();
分享我们项目中基于EF事务机制的架构

写在前面:

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 = 0;
 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(1, userCount);
31     using (var db = new MyProjectDbContext())
32     {
33         Assert.AreEqual(0, 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 = 0;
 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(1, userCount);
27     using (var db = new MyProjectDbContext())
28     {
29         Assert.AreEqual(1, 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 = 0;
 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(1, userCount);
24     using (var db = new MyProjectDbContext())
25     {
26         Assert.AreEqual(0, 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事务架构

免责声明:文章转载自《分享我们项目中基于EF事务机制的架构》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇安装ubuntu双系统abap将内表数据导出为excel文件下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

使用PowerDesign15反向生成数据库

在Pd15中建立物理模型后,可以通过反向工程直接生成数据库的表结构。主要有以下几个步骤: 1、 首先设置一下数据库配置,选择对应要使用的数据库(此处选择Sql Server 2008 R2) 2、 配置数据库连接 3、 选择新建一个连接 4、 点击添加按钮 5、 选择Sql Server 6、 输入连接的名称以及数据库服务器地址 7、 选择...

DB: 20 个数据库设计最佳实践

DB: 20 个数据库设计最佳实践 code 使用明确、统一的标明和列名,例如 School, SchoolCourse, CourceID。 数据表名使用单数而不是复数,例如 StudentCourse,而不是StudentCourses。 数据表名不要使用空格。 数据表名不要使用不必要的前缀或者后缀,例如使用School,而不是TblSchool,...

灰度环境

转载: https://www.cnblogs.com/rawboys/p/11656433.html 预发布环境: 1、只是一台服务器 2、没有真实的流量 3、连线上数据库 疑问:如果某一待上线需求,改动的是数据库表结构,怎么处理? 先把预发布环境使用的数据库切换为测试环境使用的数据库 根据实际部署过程,如果有必要,接着,可有针对性的测试下数据库的变更是...

mysql8.0 创建数据库及对应的用户

1、使用root用户登入数据库 2、创建数据库 create dababase test_database 3、创建用户 ## % 代表不限制ip ## localhost 代表只能本地访问 ## 192.168.1.1 代表只能192.168.1.1 这个固定ip访问 create user 'testuser'@'%' identified by '...

python连接mysql并进行增删改查操作

查询了菜鸟教程以及博客园等各种文章,有修改完善 转载自https://www.cnblogs.com/yuhou/p/10893056.html 自己完善部分: python连接mysql使用连接池: 一、为什么要用连接池? 1、数据库 本身有压力,并不能创建太多的并发数访问数据库,如果是大表那更加会有压力,因此限制一定的连接是更加科学的方法。2、创建和释...

建立高并发模型需要考虑的点

1、能不能通过增加机群(应用机群,服务机群)的方式去解决?好比一台机器能抗200qps, 然后你就40000qps的业务,那你最少需要200台机器,如果考虑到有机器down掉的情况,还要加备用服务器,这个具体加多少台就得去评估了。     防止出现有机器down掉,还得在每台机器上更新列表的情况,我们最好访问服务名(类似于域名),这样的话的好处就是,如果出...