如何在Entity Framework中使用事务?

28

当你有这样的代码:

Something something = new Something();
BlahEntities b = new BlahEntities()    
b.AddToSomethingSet(something);
b.SaveChanges();

如何在事务内运行此加法?

5个回答

55

ObjectContext有一个连接属性,您可以使用它来管理事务。

using (var context = new BlahEntities())
using (var tx = context.BeginTransaction())
{
    // do db stuff here...
    tx.Commit();
}

如果出现异常,则事务将被回滚。由于调用BeginTransaction()需要打开连接,因此在扩展方法中包装调用BeginTransaction()是有意义的。

public static DbTransaction BeginTransaction(this ObjectContext context)
{
    if (context.Connection.State != ConnectionState.Open)
    {
        context.Connection.Open();
    }
    return context.Connection.BeginTransaction();
}

我认为这种方法比TransactionScope更有用的情况是,当你需要访问两个数据源并且只需要对其中一个连接进行事务控制时。在这种情况下,TransactionScope会升级为分布式事务,而这可能是不必要的。


3
在EF4.1中,这不再起作用。你需要调用context.Connection.BeginTransaction - Mike Chamberlain
@Mikey Cee,它起作用了,你没有读到最后--Kim介绍了扩展方法。Kim--你是个好人!!! 你帮了我很多,非常感谢。 - greenoldman
@Kim,感谢您的帮助,真的很有用。但是,我想知道context.Connection.BeginTransaction()是否会升级为分布式事务(MSDTC)?我想避免使用MSDTC,这可能吗? - Baig
6
这是我最喜欢的答案,因为TransactionScope是一个纯理论,没有被广泛实现。 @Baig,context.Connection.BeginTransaction()永远不会升级为分布式事务(MSDTC)。 - Manitra Andriamitondra

26

你可以将代码放在事务范围内

using(TransactionScope scope = new TransactionScope())
{
    // Your code
    scope.Complete(); //  To commit.
}

TransactionScope 位于 System.Transactions 命名空间中,该命名空间位于同名程序集中(您可能需要手动将其添加到项目中)。


1
这怎么可能 - TransactionScope 如何知道 EF 上下文,反之亦然? - BlueRaja - Danny Pflughoeft
TransActionScope是一种包装器,可以包含您可以执行的所有操作:http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope%28VS.80%29.aspx - Andreas Rehm
15
小心使用TransactionScope,它会引入对MS DTC的依赖,而配置它是一件痛苦且性能代价高昂的事情。一旦在TransactionScope内有多个事务,则会启动分布式事务。这将不再仅仅是在所用提供程序上运行的简单的DbTransaction。此外,如果您使用的数据库提供程序不是Sql Server,则很可能不支持分布式事务... - miguelv
@Miguel,当您有多个事务时,它会被推广吗?难道不是当一个事务跨越多个数据库时吗? - Shiraz Bhaiji
2
即使您正在查询相同的数据库,本地事务也会升级为分布式事务。如果您使用的是SQL Server 2005(或更高版本),它将在升级之前使用轻量级事务。如果您使用不支持升级的提供程序,则即使在作用域内仅执行一个查询,也将付出性能代价。 - miguelv
它无法处理多个上下文(例如:OrderContext、ShippingContext)。 - Péter

9
我知道对于LINQ to SQL,如果没有现有的环境事务(TransactionScope是“环境”事务),数据上下文将为SubmitChanges()创建一个事务。虽然我没有看到针对LINQ to Entities的相关文件,但是我已经看到了一些行为表明Entity Framework也是如此。
只要您使用一个SubmitChanges()(L2SQL)或SaveChanges()(Linq to Entities)处理所有相关更改,就可以不使用TransactionScope。在以下情况下需要使用TransactionScope:
1. 对于一个事务保存多个更改,每个更改都使用单独的SubmitChanges/SaveChanges。 2. 在一个事务中更新多个数据源(例如,Linq和ASP.NET成员身份验证SQL提供程序)。 3. 调用可能执行自己的更新的其他方法。
我曾经在嵌套的TransactionScopes中遇到过麻烦。它们应该能够正常工作,简单的测试用例也能正常工作,但是当我进入生产代码时,“内部”事务似乎是外部事务的同一个对象。症状包括错误消息:“事务已提交,您无法再使用此事务”或“此事务对象已被处理”。这些错误发生在内部事务完成其工作后的外部事务中。

1
感谢cyloncat提供的长篇描述。我的操作是创建记录,保存记录并使用记录id创建一个文件。只有当文件成功创建后,我才会提交事务。这样说的是否合理? - pupeno
是的,这是一个很好的例子,说明何时以及如何使用事务。 - Cylon Cat

2
using System.Transactions;

using (TransactionScope scope = new TransactionScope())
{
    try
    {
        using(DataContext contextObject = new DataContext(ConnectionString))
        {
            contextObject.Connection.Open();
            // First SaveChange method.
            contextObject.SaveChanges();

            // Second SaveChange method.
            contextObject.SaveChanges();
            //--continue to nth save changes

            // If all execution successful
            scope.Complete();   
       }
    }
    catch(Exception ex)
    {
        // If any exception is caught, roll back the entire transaction and end the scope.
        scope.Dispose();
    }
    finally
    {
        // Close the opened connection
        if (contextObject.Connection.State == ConnectionState.Open)
        {
            contextObject.Connection.Close();
        }
    }
}

请点击下面的链接查看详细解释:https://msdn.microsoft.com/zh-cn/data/dn456843.aspx


0
在所有版本的Entity Framework中,每当您执行SaveChanges()来插入、更新或删除数据库时,框架都会将该操作包装在一个事务中。这个事务只持续足够长的时间来执行操作,然后完成。当您执行另一个这样的操作时,将启动一个新的事务。 对于最新的Entity Framework版本:6.0 +

了解更多信息:EntityFramework和Transaction


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接