分享我们项目中基于EF事务机制的架构
写在前面:
1. 本文中单元测试用到的数据库,在执行测试之前,会被清空,即使用空数据库。
2. 本文中的单元测试都是正确通过的。
要理解EF的事务机制,首先要理解这2个类:TransactionScope和DbContext。
DbContext是我们的数据库,通常我们会建一个类MyProjectDbContext继承自DbContext,里面包含所有的数据库表。这个类相当于定义了一个完整的数据库。
下面通过一些单元测试来看看这2个类是如何工作的。
- [Test]
- public void Can_Rollback_On_Errors_In_Different_Context()
- {
- var user1 = Mock.Users.Random();
- var user2 = Mock.Users.Random();
- user2.FirstName = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
- var userCount = ;
- try
- {
- using (var scope = new TransactionScope())
- {
- using (var db = new MyProjectDbContext())
- {
- db.Users.Add(user1);
- db.SaveChanges();
- userCount = db.Users.Count();
- }
- using (var db = new MyProjectDbContext())
- {
- db.Users.Add(user2);
- db.SaveChanges();//will throw exception
- }
- scope.Complete();
- }
- }
- catch(Exception)
- {
- }
- Assert.AreEqual(, userCount);
- using (var db = new MyProjectDbContext())
- {
- Assert.AreEqual(, db.Users.Count());
- }
- }
注意第一个assert,userCount是等于1的,也就是说第一个db.SaveChanges()是顺利执行了的。但是看看第二个assert,数据库里面却没有user记录。这就是使用TransactionScope得到的真正的事务机制。
再看一个测试:
- [Test]
- public void Cannot_Rollback_Without_Scope()
- {
- var user1 = Mock.Users.Random();
- var user2 = Mock.Users.Random();
- user2.FirstName = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
- var userCount = ;
- try
- {
- using (var db = new MyProjectDbContext())
- {
- db.Users.Add(user1);
- db.SaveChanges();
- userCount = db.Users.Count();
- }
- using (var db = new MyProjectDbContext())
- {
- db.Users.Add(user2);
- db.SaveChanges();//will throw exception
- }
- }
- catch (Exception)
- {
- }
- Assert.AreEqual(, userCount);
- using (var db = new MyProjectDbContext())
- {
- Assert.AreEqual(, db.Users.Count());
- }
- }
这个测试跟上面的测试差不多,唯一的区别就是没有使用TransactionScope把两个DbContext包起来。于是每个DbContext成为独立的事务。
再来看一个测试:
- [Test]
- public void Shouldnot_SaveToDB_As_ScopeNotComitted()
- {
- var user1 = Mock.Users.Random();
- var userCount = ;
- try
- {
- using (var scope = new TransactionScope())
- {
- using (var db = new MyProjectDbContext())
- {
- db.Users.Add(user1);
- db.SaveChanges();
- userCount = db.Users.Count();
- }
- //scope.Complete();
- }
- }
- catch (Exception)
- {
- }
- Assert.AreEqual(, userCount);
- using (var db = new MyProjectDbContext())
- {
- Assert.AreEqual(, db.Users.Count());
- }
- }
}
这个测试表明,一旦DbContext被TransactionScope包起来之后,那么scope必须要调用scope.Complete()才能将数据更新到数据库。
基于上面的这些知识,我们可以很容易为EF搭建支持真正事务的框架。下面我简单介绍下我们的项目架构(EF CodeFirst, MVC)。
基于EF事务机制的架构
Domain层:
定义数据实体类,即数据库中的表。定义继承自DbContext的MyProjectDbContext。
Service层:
主要用于封装所有对数据库的访问。例子代码如下:
- public List<User> GetAllUsers()
- {
- using (var db = new MyProjectDbContext())
- {
- return db.Users.ToList();
- }
- }
上面这段代码中注意要使用using,否则DbContext的延迟加载功能会在controller层被调用。加了using之后,可以避免在controller层对数据库的直接访问。
Controller层:
调用service层的代码从数据库中得到数据,返回给UI。例子:
- public ActionResult GetAllUsers()
- {
- var users = IoC.GetService<IUserService>().GetAll();
- return View(users);
- }
同时将UI传回来的数据更新到数据库,这时如果需要调用多个service来更新数据库,那么就需要用到事务。例子:
- public ActionResult DeleteUser(int userId)
- {
- try
- {
- using (var scope = new TransactionScope())
- {
- IoC.GetService<IUserService>().DeleteLogs(userId);
- IoC.GetService<IUserService>().DeleteUser(userId);
- scope.Complete();
- return View();
- }
- }
- catch(Exception)
- {
- }
- return View();
- }
通常情况下,我们会在MyControllerBase里面加一个 ActionResult TryScope(Action action)的方法,这样在子类里面就可以不用写try-catch了。
对于EF更深层的机制,我了解的也不多。欢迎大家讨论!
分享我们项目中基于EF事务机制的架构的更多相关文章
- 分享我们项目中基于EF事务机制的架构 【转载】
http://www.cnblogs.com/leotsai/p/how-to-use-entity-framework-transaction-scope.html 写在前面: 1. 本文中单元测试 ...
- [翻译 EF Core in Action 1.10] 应该在项目中使用EF Core吗?
Entity Framework Core in Action Entityframework Core in action是 Jon P smith 所著的关于Entityframework Cor ...
- 在项目中部署redis的读写分离架构(包含节点间认证口令)
#### 在项目中部署redis的读写分离架构(包含节点间认证口令) ##### 1.配置过程 --- 1.此前就是已经将redis在系统中已经安装好了,redis utils目录下,有个redis ...
- 采用EntLib5.0(Unity+Interception+Caching)实现项目中可用的Caching机制
看了园子里很多介绍Caching的文章,多数都只介绍基本机制,对于Cache更新和依赖部分,更是只简单的实现ICacheItemRefreshAction接口,这在实际项目中是远远不够的.实际项目中, ...
- visual studio 项目中使用EF创建的数据库,后续更新数据库操作(生产已经部署,不能删除数据库重新创建)
情景:SharePoint项目(其他类型的项目道理也一样),数据库是用EF(版本:6.0.0.0)创建的,生产环境已经使用,所以后续修改数据库,只能通过更新来实现. 下面是具体的操作方式: 1.vis ...
- 关于项目中的DAL数据接入层架构设计
摘要:项目中对关系型数据库的接入再寻常不过,也有海量的ORM工具可供选择,一个一般性的DAL数据接入层的结构却大同小异,这里就分享一下使用Hibernate.Spring.Hessian这三大工具对D ...
- 实际项目中遇到EF实体类的操作问题及操作方法
之前一直做ASP,都是直接写数据库操作语句,但是现在使用linq或者EF了,具体数据库操作不会了,遇到几个问题,然后经过查找资料解决了,记录一下. 一.遇到序列化问题 遇到循环引用问题,我的项目是一个 ...
- 在Blazor Server 项目中使用 EF Core Sqlite
按照教程创建了一个 Blazor Server 项目 教程地址: https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/build-a-blaz ...
- vue项目中基于D3.js实现桑基图功能
前端实现数据可视化的方案有很多种,以前都是使用百度的echarts,使用起来很方便,直接按照特定的数据格式输入,就能实现相应的效果,虽然使用方便,但是缺点就是无法自定义一些事件操作,可自由发挥的功能很 ...
随机推荐
- Blender 之修改器代码分析
Blender的修改器(modifier)模块,默认界面右下块(Property)面板的扳手,分类(修改.生成.形变.模拟)列出所有的修改器.也可以空格键 ...
- Java基础学习(三)
/* java中的八种基本数据类型: 整数: byte . short . int . long 小数: float double 字符: char 布尔: boolean 字符串的类型: Strin ...
- Datazen配置
Datazen是被微软收购的移动端全平台的数据展现解决方案.此篇主要介绍其服务器端的配置过程. 在上一篇的基础安装完成之后,在浏览器敲入如下地址进入系统的控制面板(留意安装的时候配置的是80 web端 ...
- How GitHub Works《Github是如何工作的?》
https://github.com/blog/920-how-github-works 如果你想知道Github是如何工作的,你可以看查看Zach Holman(@holman)的三篇文章: Hou ...
- UWP学习记录1-开端
UWP学习记录1-开端 1.背景 针对不同基础的人,学习的路线自然是不同的.这篇文章记录的是我个人的学习路线,或者说笔记.我对自己的技术状态的定义是: A.有很好的windows平台编程基础: B.有 ...
- 关于ubuntu16无线网卡RTL8723BE频繁掉线及信号不足的解决办法
最近在新电脑上装了ubuntu16,结果wifi经常连不上,连上了过段时间就掉线,路由器就在电脑的旁边,而且信号非常的若. 但是windows系统没有任何问题,所以就在网上找解决办法,也按照网上的方法 ...
- ubuntu14.04下配置Java环境以及安装最新版本的eclipse
首先是配置JDK 步骤一:下载最新版本的JDK,链接:http://www.oracle.com/technetwork/java/javase/downloads/index.html 步骤二:首先 ...
- JS简单解决并发量
经常在写代码的时候碰到这样的场景:页面初始化时显示loading页,同时启动多个ajax并发请求获取数据,当每个ajax请求返回时结束loading. 举个例子,一个下订单的页面,要查询常用地址信息. ...
- Git 简介
版本控制 什么是版本控制? 我需要版本控制吗? - 如果你还没使用过版本控制系统,或许你会有以上疑问,甚至更多疑问.希望后面的回答能让你喜欢上版本控制系统,喜欢上Git. 什么是版本控制:顾名思义,版 ...
- 【MongoDB初识】-结合C#简单使用,驱动2.x
public static Students GetEntityByName(string conStr, string userName = "bj") { Students s ...