EF 事务(转载)
事务简单用法
文章一:https://www.cnblogs.com/wujingtao/p/5407821.html
1EF事务
事务就是确保一次数据库操作,所有步骤都成功,如果哪一步出错了,整个操作都将回滚。
在EF使用事务有两种方案,一种是EF自带的.BeginTransaction()方法,另一种是使用TransactionScope类。
使用.BeginTransaction()
使用.BeginTransaction()实现事务
class Program
{
static void Main(string[] args)
{
using (var db = new DBModel())
{
var tran = db.Database.BeginTransaction(); //开启事务 try
{
var student = db.students.FirstOrDefault(s => s.name == "萝莉");
db.students.Remove(student); //删除萝莉 db.SaveChanges(); tran.Commit(); //必须调用Commit(),不然数据不会保存
}
catch (Exception ex)
{
tran.Rollback(); //出错就回滚
}
}
}
}
使用TransactionScope类
使用之前记得引入System.Transactions.dll
使用TransactionScope
class Program
{
static void Main(string[] args)
{
using (var db = new DBModel())
{
using (var tran = new TransactionScope()) //开启事务
{
var student = db.students.FirstOrDefault(s => s.name == "萝莉");
db.students.Remove(student); //删除萝莉 db.SaveChanges(); tran.Complete(); //必须调用.Complete(),不然数据不会保存
} //出了using代码块如果还没调用Complete(),所有操作就会自动回滚
}
}
}
文章二:https://www.cnblogs.com/CreateMyself/p/4787856.html#!comments
事务复杂用法
1概念
在开始学习事务之前我们先了解两个概念:
- Database.BeginTransaction():它是在一个已存在的DbContext上下文中对于我们去启动和完成transactions的一种简单方式,它允许多个操作组合存在在相同的transaction中,所以要么提交要么全部作为一体回滚,同时它也允许我们更加容易的去显示指定transaction的隔离级别。
- Dtabase.UseTransaction():它允许DbContext上下文使用一个在EF实体框架之外启动的transaction。
2默认事务
当我们调用SaveChanges方法来执行增、删、改时其操作内部都用一个transaction包裹着(自动完成的)。如下图,当添加数据时:

- 对于上下文中的 ExecuteSqlCommand() 方法默认情况下也是用transaction包裹着命令(Command),其有重载我们可以显示指定执行事务还是不确定执行事务。
- 在此上两种情况下,事务的隔离级别是数据库提供者认为的默认设置的任何隔离级别,例如在SQL Server上默认是READ COMMITED(读提交)。
- EF对于任何查询都不会用transaction来进行包裹。
在EF 6.0版本以上,EF一直保持数据库连接打开,因为要启动一个transaction必须是在数据库连接打开的前提下,同时这也就意味着我们执行多个操作在一个transaction的唯一方式是要么使用 TransactionScope 要么使用 ObjectContext.Connection 属性并且启动调用Open()方法以及BeginTransaction()方法直接返回EntityConnection对象。如果你在底层数据库连接上启动了transaction,再调用API连接数据库可能会失败。
3同一上下文中使用transaction
Database.BeginTransaction有两种重载——一种是显示指定隔离级别,一种是无参数使用来自于底层数据库提供的默认隔离级别,两种都是返回一个DbContextTransaction对象,该对象提供了事务提交(Commint)以及回滚(RollBack)方法直接表现在底层数据库上的事务提交以及事务回滚上。
DbContextTransaction一旦被提交或者回滚就会被Disposed,所以我们使用它的简单的方式就是使用using(){}语法,当using构造块完成时会自动调用Dispose()方法。
根据上述我们现在通过两个步骤来对学生进行操作,并在同一transaction上提交。如下:

using (var ctx = new EntityDbContext())
{ using (var ctxTransaction = ctx.Database.BeginTransaction())
{ try
{
ctx.Database.Log = Console.WriteLine; ctx.Database.ExecuteSqlCommand("update student set name='xpy0929'"); var list = ctx.Set<Student>().Where(p => p.Name == "xpy0929").ToList(); list.ForEach(d =>
{ d.Name = "xpy0928"; }); ctx.SaveChanges(); ctxTransaction.Commit();
}
catch (Exception)
{
ctxTransaction.Rollback();
} }
}

我们通过控制台输出SQL日志查看提交事务成功如下:

【注意】 要开始一个事务必须保持底层数据库连接是打开的,如果数据库不总是打开的我们可以通过 BeginTransaction() 方法将打开数据库连接,如果 DbContextTransaction 打开了数据库,当调用Disposed()方法时将会关闭数据库连接。
4注意事项
1一定要用using包裹,保证每次上下文会新建和释放,这个上下文不可与其它方法共用。
2当用EF上下文中的 Database.ExecuteSqlCommand 方法来对数据库进行如下操作时

using (var ctx = new EntityDbContext())
{ var sqlCommand = String.Format("ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE", "DBConnectionString");
ctx.Database.ExecuteSqlCommand(sqlCommand); }

此时将会报错如下:

上述已经讲过此方法会被Transaction包裹着,所以导致出错,但是此方法有重载,我们进行如下设置即可
ctx.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction,sqlCommand);
5将一个已存在的事务添加到上下文中
有时候我们可能需要事务的作用域更加广一点,当然是在同一数据库上但是是在EF之外完全进行操作。基于此,此时我们必须手动打开底层的数据库连接来启动事务,同时通知EF使用我们手动打开的连接来使现有的事务连接在此连接上,这样就达到了在EF之外使用事务的目的。
为了实现上述在EF之外使用事务我们必须在DbContext上下文中的派生类的构造器中关闭自身的连接而使用我们传入的连接。
第一步
上下文中关闭EF连接使用底层连接。
代码如下:
public EntityDbContext(DbConnection con)
: base(con, contextOwnsConnection: false)
{ }
第二步
启动Transcation(如果我们想避免默认设置我们可以手动设置隔离级别),通知EF一个已存在的Transaction已经在我们手动的设置的底层连接上启动。

using (var con = new SqlConnection("ConnectionString"))
{
using (var SqlTransaction = con.BeginTransaction())
{
using (var ctx = new EntityDbContext(con))
{
}
}
}

第三步
因为此时是在EF实体框架外部执行事务,此时则需要用到上述所讲的 Database.UseTransaction 将我们的事务对象传递进去。
ctx.Database.UseTransaction(SqlTransaction);
此时我们将能通过SqlConnection实例来自由执行数据库操作或者说是在上下文中,执行的所有操作都是在一个Transaction上,而我们只负责提交和回滚事务并调用Dispose方法以及关闭数据库连接即可。
至此给出完整代码如下:

using (var con = new SqlConnection("ConnectionString"))
{
con.Open();
using (var SqlTransaction = con.BeginTransaction())
{
try
{
var sqlCommand = new SqlCommand();
sqlCommand.Connection = con;
sqlCommand.Transaction = SqlTransaction;
sqlCommand.CommandText =
@"update student set name = 'xpy0929'";
sqlCommand.ExecuteNonQuery();
using (var ctx = new EntityDbContext(con))
{
ctx.Database.UseTransaction(SqlTransaction);
var list = ctx.Set<Student>().Where(d => d.Name == "xpy0929").ToList();
list.ForEach(d => {
d.Name = "xpy0928";
});
ctx.SaveChanges();
}
SqlTransaction.Commit();
}
catch (Exception)
{
SqlTransaction.Rollback();
}
}
}

【注意】你可以设置 ctx.Database.UseTransaction(null); 为空来清除当前EF中的事务,如果你这样做了,那么此时EF既不会提交事务也不会回滚现有的事务,除非你清楚这是你想做的 ,否则请谨慎使用。
6 TransactionScope Transactions
在msdn上对 TransactionScope 类定义为是:类中的代码称为事务性代码。
我们将上述代码包含在如下代码中,则此作用域里的代码为事务性代码
using ( var scope = new TransactionScope(TransactionScopeOption.Required))
{ }
【注意】此时SqlConnection和EF实体框架都使用 TransactionScope ,因此此时将被会一起提交。
在.NET 4.5.1中 TransactionScope 能够和异步方法一起使用通过TransactionScopeAsyncFlowOption的枚举来启动。
通过如下实现:
using (var scope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{}
接着就是将数据库连接的打开方法(Open)、查询方法(ExecuteNonQuery)、以及上下文中保存的方法(SaveChanges)都换为对应的异步方法(OpenAsync)、(ExecuteNonQueryAsync)以及(SaveChangesAsync)即可
EF 事务(转载)的更多相关文章
- 分享我们项目中基于EF事务机制的架构 【转载】
http://www.cnblogs.com/leotsai/p/how-to-use-entity-framework-transaction-scope.html 写在前面: 1. 本文中单元测试 ...
- 分享我们项目中基于EF事务机制的架构
写在前面: 1. 本文中单元测试用到的数据库,在执行测试之前,会被清空,即使用空数据库. 2. 本文中的单元测试都是正确通过的. 要理解EF的事务机制,首先要理解这2个类:TransactionSco ...
- EF 事务
http://yanwushu.byethost7.com/?p=87 1. EF对事务进行了封装:context的saveChange()是有事务性的. 2. 依赖多个不同的Context的操作(即 ...
- EF 事务(非分布式事务)
在EF 中怎么使用事务? 这个问题纠结了我好久,直到有人跟我一起讨论,我和同事一起讨论查资料. 查的好多资料都是使用 TransactionScope,用 TransactionScope 可处理分布 ...
- EF事务嵌套
EF中采用的是数据上下文DbContext,当针对数据库的所有操作共用一个数据上下文的时候,会使用同一个连接对象,因此连接打开一次,最后Save的时候关闭连接,避免了频繁的创建连接对象打开关闭,这在一 ...
- MySql EF事务using不会自动 Rollback的bug
EF to MySql一般都是用using最后Commit,一直以为最后没Commit,当using调用Dispose会自动Rollback,没想到这儿有个坑,mysql有个bug并不会Rollbac ...
- 第六章 springboot + 事务(转载)
本篇博客转发自:http://www.cnblogs.com/java-zhao/p/5350106.html 在实际开发中,其实很少会用到事务,一般情况下事务用的比较多的是在金钱计算方面. myba ...
- 使用Atomikos Transactions Essentials实现多数据源JTA分布式事务--转载
原文:http://www.ite/topic/122700 9.17 update:使用NonXADataSourceBean. Mysql在5.0版本和Connecter/J5.0版本后提供了XA ...
- EF事务封装
public class EFTransaction:ITransaction { DbContextTransaction originalTransaction = null; MyDbConte ...
随机推荐
- 一窍懂PID
这是学习PID时的理解,做个笔记! 一:首选,说一下一些基本概念,为了更好理解. PID:就是对输入偏差进行比例积分微分运算,运算的叠加结果去控制执行机构. P(proportion):比例,就是对输 ...
- LeetCode刷题:第四题 寻找两个有序数组的中位数
题目描述: 给定两个大小为 m 和 n 的有序数组 nums1 和 nums2. 请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n)). 你可以假设 nums1 和 ...
- 【2016年终大典】i春秋一年中不可错过的安全精华
这是一个24小时不下课的安全技术大学堂, 每分钟250条学习状态发布, 每天迎接3万求知若渴的用户, 最高同时在线人数超过2万人: 这是一个知识分享的聚宝盆, 安全技术课程208门.2138节.427 ...
- Javascript高级编程学习笔记(22)—— 对象继承
继承是所有面向对象的语言最让人津津乐道的概念 许多面向对象的语言都支持两种实现继承的方式: 1.接口继承 2.实现继承 由于ECMAScript中没有函数签名,所以自然也是不支持接口继承 所以JS中能 ...
- 使用CSS样式的三种方式
外部样式表 当样式需要被应用到很多页面的时候,外部样式表将是理想的选择.使用外部样式表,你就可以通过更改一个文件来改变整个站点的外观. 内部样式表 当单个文件需要特别样式时,就可以使用内部样式表.你可 ...
- Core 接口发布报错
An error occurred while starting the application 提示:启动应用程序时发生错误 这个错误在Startup启动项里面 要找具体的报错位置 如下 这样就可以 ...
- pickle模块及其API
模块:pickle 所包含API列表: pickle.dumps : 把任意对象序列化成一个bytes pickle.dump : 直接把对象序列化后写入一个file-like Object pick ...
- ModelState 错误信息输出
在MVC的项目中,我们通常情况下,为了方便(偷懒),会直接使用 !ModelState.IsValid 来判断实体的验证是否正确,但是这样对于用户的体验是不好的,当填写的内容比较多的时候,用户需要自己 ...
- 人生苦短,我用Python(1)
一.人生苦短,我用Python 在文章的开头给大家介绍一下Python语言,作者Guido von Rossum.对,就是图中的“人生苦短我用Python”那哥们.你可能以为我会用很多文字介绍Pyth ...
- python基础-循环语句(5)
一.循环语句介绍 一般情况下,需要多次重复执行的代码,都可以用循环的方式来完成 循环不是必须要使用的,但是为了提高代码的重复使用率,所以有经验的开发者都会采用循环 二.常见的循环形式 while循环 ...