分享我们项目中基于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,使用起来很方便,直接按照特定的数据格式输入,就能实现相应的效果,虽然使用方便,但是缺点就是无法自定义一些事件操作,可自由发挥的功能很 ...
随机推荐
- Qt基本框架介绍
#include <QApplication>#include <QWidget> int main(int argc, char *argv[]){ QApplication ...
- 简单爬虫,突破IP访问限制和复杂验证码,小总结
简单爬虫,突破复杂验证码和IP访问限制 文章地址:http://www.cnblogs.com/likeli/p/4730709.html 好吧,看题目就知道我是要写一个爬虫,这个爬虫的目标网站有 ...
- UGUI全面实践教程
UGUI全面实践教程 试读文档下载地址:http://pan.baidu.com/s/1hq3UYGk 介绍:UGUI是Unity官方推出的最新UI系统.本教程为国内唯一的UGUI专向资料.本教程 ...
- iOS路径沙盒文件管理(转载)
iOS路径沙盒文件管理,看到博主总结的很好,转载过来,原文:http://www.aichengxu.com/view/35264 一.iOS中的沙盒机制 iOS应用程序只能对自己创建的文件系统读取文 ...
- 【转】CentOS上部署PPTP和L2TP over IPSec简要笔记
PPTP部署 安装 PPTP 需要 MPPE 和较高版本的 ppp ( > 2.4.3 ) 支持,不过 CentOS 5.0/RHEL 5 的 2.6.18 内核已经集成了 MPPE 和高版本的 ...
- Visual Studio 常用快捷键
作为一个使用VisualStudio的程序员,使用快捷键会为你的开发提供助力. 下附个人开发过程中感觉比较实用的快捷键: 开始运行"devenv",启动相应版本的VisualStu ...
- liunux 修改hostname
最近鼓捣Oracle,记录些技巧 修改hostname # vim /ect/hosts # vim /etc/sysconfig/network 修改hostname # service netwo ...
- 弱省互测#0 t2
题意 给定两个字符串 A 和 B,求下面四个问题的答案: 1.在 A 的子串中,不是 B 的子串的字符串的数量. 2.在 A 的子串中,不是 B 的子序列的字符串的数量. 3.在 A 的子序列中,不是 ...
- init shutdown reboot poweroff halt区别
init 首先看看LINUX系统几种运行级别# 0 - 停机(千万别把initdefault设置为0,否则系统永远无法启动)# 1 - 单用户模式# 2 - 多用户,没有 NFS# 3 - 完全多用户 ...
- dubbo 常见错误
1. Caused by: java.lang.reflect.MalformedParameterizedTypeException 或 Caused by: java.lang.NoSuchMet ...