C# 使用事务或 SaveChanges(false) 和 AcceptAllChanges()?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/815586/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using Transactions or SaveChanges(false) and AcceptAllChanges()?
提问by mark smith
I have been investigating transactions and it appears that they take care of themselves in EF as long as I pass false
to SaveChanges()
and then call AcceptAllChanges()
if there are no errors:
我一直在调查交易,似乎他们在 EF 中照顾自己,只要我传递false
给SaveChanges()
然后调用,AcceptAllChanges()
如果没有错误:
SaveChanges(false);
// ...
AcceptAllChanges();
What if something goes bad? don't I have to rollback or, as soon as my method goes out of scope, is the transaction ended?
如果出现问题怎么办?我不必回滚,或者一旦我的方法超出范围,事务就结束了吗?
What happens to any indentiy columns that were assigned half way through the transaction? I presume if somebody else added a record after mine before mine went bad then this means there will be a missing Identity value.
在事务中途分配的任何 indenty 列会发生什么情况?我认为如果在我的记录变坏之前有人在我的记录之后添加了一条记录,那么这意味着将缺少 Identity 值。
Is there any reason to use the standard TransactionScope
class in my code?
有什么理由TransactionScope
在我的代码中使用标准类吗?
采纳答案by Alex James
With the Entity Framework most of the time SaveChanges()
is sufficient. This creates a transaction, or enlists in any ambient transaction, and does all the necessary work in that transaction.
大部分时间使用实体框架SaveChanges()
就足够了。这将创建一个事务,或加入任何环境事务,并在该事务中完成所有必要的工作。
Sometimes though the SaveChanges(false) + AcceptAllChanges()
pairing is useful.
有时虽然SaveChanges(false) + AcceptAllChanges()
配对很有用。
The most useful place for this is in situations where you want to do a distributed transaction across two different Contexts.
最有用的地方是在您想要跨两个不同的上下文进行分布式事务的情况下。
I.e. something like this (bad):
即这样的事情(坏):
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();
}
If context1.SaveChanges()
succeeds but context2.SaveChanges()
fails the whole distributed transaction is aborted. But unfortunately the Entity Framework has already discarded the changes on context1
, so you can't replay or effectively log the failure.
如果 context1.SaveChanges()
成功但context2.SaveChanges()
失败,则整个分布式事务将中止。但不幸的是,实体框架已经放弃了对 的更改context1
,因此您无法重播或有效地记录失败。
But if you change your code to look like this:
但是,如果您将代码更改为如下所示:
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();
}
While the call to SaveChanges(false)
sends the necessary commands to the database, the context itself is not changed, so you can do it again if necessary, or you can interrogate the ObjectStateManager
if you want.
虽然调用SaveChanges(false)
将必要的命令发送到数据库,但上下文本身并没有改变,因此您可以在必要时再次执行此操作,或者您可以根据需要询问ObjectStateManager
。
This means if the transaction actually throws an exception you can compensate, by either re-trying or logging state of each contexts ObjectStateManager
somewhere.
这意味着如果事务实际上抛出异常,您可以通过重试或记录每个上下文的状态来补偿ObjectStateManager
。
回答by user3885816
If you are using EF6 (Entity Framework 6+), this has changed for database calls to SQL.
See: http://msdn.microsoft.com/en-us/data/dn456843.aspx
如果您使用的是 EF6(实体框架 6+),这已更改为对 SQL 的数据库调用。
请参阅:http: //msdn.microsoft.com/en-us/data/dn456843.aspx
use context.Database.BeginTransaction.
使用 context.Database.BeginTransaction。
From MSDN:
来自 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 } } }
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 } } }
回答by eMeL
Because some database can throw an exception at dbContextTransaction.Commit() so better this:
因为某些数据库可以在 dbContextTransaction.Commit() 处抛出异常所以更好:
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();
}
}
}