为什么不在事务中使用Entity Framework(范例)

・5 分钟阅读

EntityFramework

这是来自微软的.NET ORM Mapper框架,帮助你以面向对象的方式与数据库进行对话,维基百科

数据库事务

按照定义,数据库事务必须是原子的,一致的,独立的和持久的,数据库从业者经常使用缩写来引用数据库事务的这些属性,数据库环境中的事务有两个主要目的:


  1. 为确保正确恢复,并且保持数据库一致,即使在系统失败时也保持数据库一致,提供可靠的工作单元,当执行停止(完全或部分),并且数据库上的许多操作仍然未完成时,状态不清楚,
  2. 并发访问数据库的程序之间的隔离,如果未提供这种隔离,程序的结果可能是错误的,维基百科

.NET事务

可以通过不同的框架以不同的方式使用.NET事务来支持事务,.NET事务本身与数据库没有任何关系,MSDN

.NET事务和EntityFramework

如果在打开的TransactionScope期间使用Entity Framework,EntityFramework将使用下一个命令打开一个新事务,然后将发送到数据库(CRUD操作)。

请考虑以下代码块:


using (var transaction = new System.Transactions.TransactionScope())
{
 // DBC = Database Command

 // create the database context
 var database = new DatabaseContext();

 // search for the user with id #1
 // DBC: BEGIN TRANSACTION
 // DBC: select * from [User] where Id = 1
 var userA = database.Users.Find(1);
 // DBC: select * from [User] where Id = 2
 var userB = database.Users.Find(2);
 userA.Name ="Admin";

 // DBC: update User set Name ="Admin" where Id = 1
 database.SaveChanges();

 userB.Age = 28;
 // DBC: update User set Age = 28 where Id = 2
 database.SaveChanges();

 // DBC: COMMIT TRANSACTION
 transaction.Complete();
}

https://gist.github.com/SeriousM/e6b30db2b21e7e602655#file-bad_example-cs

database.SaveChanges()调用向数据库发送更改,并且执行它们,但是,它们并不真正持久,因为你在数据库事务范围内,transaction.Complete()实际上完成了数据库事务,你的数据被保存。

这种行为很酷而且非常有用?

不,绝对不能这么干。

为什么不使用.NET事务和EntityFramework

默认隔离模式是read committed,适合于99%的需要,如下所示,当你想保存对数据库(创建,更新,删除)所做的更改时,EntityFramework足够智能地创建一个事务,你可以确保所有的内容都将被保存,或者每一个更改都将被丢弃(原子性 )。

通过使用EntityFramework中的事务,可以更改这个行为,并且强制在serializable隔离模式中执行事务范围中的每个CRUD操作,在您的交易过程中,任何流程都无法访问您所触及的表(甚至可以从中读取)。这可能导致死锁的很快,你会不惜一切代价避免它们!

在没有显式使用事务的情况下,代码就是这样的:


// DBC = Database Command

// create the database context
var database = new DatabaseContext();

// search for the user with id #1
// DBC: select * from [User] where Id = 1
var userA = database.Users.Find(1);
// DBC: select * from [User] where Id = 2
var userB = database.Users.Find(2);
userA.Name ="Admin";
userB.Age = 28;

// DBC: BEGIN TRANSACTION
// DBC: update User set Name ="Admin" where Id = 1
// DBC: update User set Age = 28 where Id = 2
// DBC: COMMIT TRANSACTION
database.SaveChanges();

https://gist.github.com/SeriousM/e6b30db2b21e7e602655#file-good_example-cs

经验法则:每个任务只保存一次,不使用事务。

编辑:感谢@seimur - 一个线程应该可以访问一个DbContext实例,该实例在Web应用程序中最有效,其中每个请求都充当一个线程。在Windows应用程序中,每个命令或任务都应该有一个不共享的DbContext ,如果你在线程之间共享DbContext,可能会遇到一些问题,比如,会读入到外部事务中。

Jiaoyang75 profile image