使用事务或SaveChanges(false)和AcceptAllChanges()?
我一直在调查交易,看来只要我把false
传递给SaveChanges()
,然后在没有错误的情况下调用AcceptAllChanges()
,他们就会在EF中自我照顾。
SaveChanges(false); // ... AcceptAllChanges();
如果有什么坏事怎么办? 我不必回滚,或者一旦我的方法超出范围,交易就结束了吗?
通过交易中途分配的任何专栏会发生什么? 我猜想如果有人在我之前添加了一个logging我的坏了,那么这意味着将有一个失踪的身份价值。
有没有任何理由在我的代码中使用标准的TransactionScope
类?
大部分情况下,使用Entity Framework SaveChanges()
就足够了。 这创build了一个交易,或者进入任何环境交易,并且在交易中完成所有必要的工作。
有时虽然SaveChanges(false) + AcceptAllChanges()
配对是有用的。
对于这种情况最有用的地方是在两个不同的上下文中执行分布式事务的情况。
即这样的事情(坏):
using (TransactionScope scope = new TransactionScope()) { //Do something with context1 //Do something with context2 //Save and discard changes context1.SaveChanges(); //Save and discard changes context2.SaveChanges(); //if we get here things are looking good. scope.Complete(); }
如果context1.SaveChanges()
成功,但context2.SaveChanges()
失败,则整个分布式事务将中止。 但不幸的是,entity framework已经放弃了context1
上的更改,因此无法重播或有效logging失败。
但是,如果您将代码更改为如下所示:
using (TransactionScope scope = new TransactionScope()) { //Do something with context1 //Do something with context2 //Save Changes but don't discard yet context1.SaveChanges(false); //Save Changes but don't discard yet context2.SaveChanges(false); //if we get here things are looking good. scope.Complete(); context1.AcceptAllChanges(); context2.AcceptAllChanges(); }
虽然调用SaveChanges(false)
将必要的命令发送到数据库,但上下文本身不会更改,因此可以根据需要再次执行该操作,也可以在需要时询问ObjectStateManager
。
这意味着如果事务实际上抛出了一个exception,你可以通过重新尝试或logging每个上下文ObjectStateManager
状态来补偿。
看到我的 博客文章更多。
如果您使用的是EF6(entity framework6+),则数据库对SQL的调用发生了改变。
请参阅: http : //msdn.microsoft.com/en-us/data/dn456843.aspx
使用context.Database.BeginTransaction。
来自MSDN:
using (var context = new BloggingContext()) { using (var dbContextTransaction = context.Database.BeginTransaction()) { try { context.Database.ExecuteSqlCommand( @"UPDATE Blogs SET Rating = 5" + " WHERE Name LIKE '%Entity Framework%'" ); var query = context.Posts.Where(p => p.Blog.Rating >= 5); foreach (var post in query) { post.Title += "[Cool Blog]"; } context.SaveChanges(); dbContextTransaction.Commit(); } catch (Exception) { dbContextTransaction.Rollback(); //Required according to MSDN article throw; //Not in MSDN article, but recommended so the exception still bubbles up } } }
因为有些数据库可以在dbContextTransaction.Commit()处引发exception,所以最好这样做:
using (var context = new BloggingContext()) { using (var dbContextTransaction = context.Database.BeginTransaction()) { try { context.Database.ExecuteSqlCommand( @"UPDATE Blogs SET Rating = 5" + " WHERE Name LIKE '%Entity Framework%'" ); var query = context.Posts.Where(p => p.Blog.Rating >= 5); foreach (var post in query) { post.Title += "[Cool Blog]"; } context.SaveChanges(false); dbContextTransaction.Commit(); context.AcceptAllChanges(); } catch (Exception) { dbContextTransaction.Rollback(); } } }
通常(99.9%的时间)在执行sql语句之前或之中抛出exception。 之后运行AcceptAllChanges,以确保所有语句都已成功完成。
DbContext的SaveChanges调用方法automaticaly(至less在EF6中),所以你不需要直接调用它。 唯一有用的地方是当你在多个上下文中分割应用程序,并且你想在事务中保存至less两个。 如果上次保存失败,则可能需要重试以前的保存。
我build议在任何时候都使用交易,除非您完全确定您的应用不会增长或添加更多function。