如何在dapper.net上使用交易?


106

我想在多个表上运行多个insert语句。我正在使用dapper.net。我看不到使用dapper.net处理交易的任何方法。

请通过dapper.net分享您有关如何使用交易的想法。

Answers:


107

这里的代码片段:

using System.Transactions;    
....    
using (var transactionScope = new TransactionScope())
{
    DoYourDapperWork();
    transactionScope.Complete();
}

请注意,您需要添加对System.Transactions程序集的引用,因为默认情况下未引用该程序集。


7
是否有必要显式回滚错误,或者System.Transactions是否自动处理该错误?
诺伯特·诺伯森

6
@NorbertNorbertson在Dispose()方法中自动执行。如果Complete()尚未调用,则事务将回滚。
the_joric

4
值得一提的是另一个答案(stackoverflow.com/a/20047975/47672):必须在TransctionScopeusing块内部打开连接,以防您选择此答案。
6

2
另请参阅(stackoverflow.com/a/20047975/444469)-DoYouDapperWork(执行,查询等)需要参数中的事务。
Matthieu

如果有问题,是否会自动调用回滚?
gandalf

91

我更喜欢使用更直观的方法,直接从连接获取事务:

// This called method will get a connection, and open it if it's not yet open.
using (var connection = GetOpenConnection())
using (var transaction = connection.BeginTransaction())
{
    connection.Execute(
        "INSERT INTO data(Foo, Bar) values (@Foo, @Bar);", listOf5000Items, transaction);
    transaction.Commit();
}

@ANeves:好吧,我们可能正在使用不同的Dapper框架,因为这个框架有:github.com/StackExchange/dapper-dot-net
andrecarlucci 2014年

25
必须在.begintransaction之前调用connection.open()
永恒的

除非您在transactionscope中打开连接,否则不会自动在transactionscope中征用连接。我不知道您的代码如何工作,如果GetOpenConnection在事务范围内以某种方式神奇地打开了自己,但我敢打赌它不会
Erik

@ErikBergstedt,您是说连接只有在我们调用它之后才必须打开吗?如果真是这样,此扩展方法将促进错误使用事务。(IMO,它甚至应该抛出“连接已经打开后无法打开事务”。).BeginTransaction()
ANeves 2015年

2
最好将事务作为参数包含在中Execute,因为这是必需的。
Arve Systad

19

TransactionScope由于Dapper仅运行ADO.NET命令,因此您应该能够使用。

using (var scope = new TransactionScope())
{
   // insert
   // insert
   scope.Complete();
}

8

考虑到所有表都在单个数据库中,我不同意TransactionScope此处一些答案中建议的解决方案。请参阅答案。

  1. TransactionScope通常用于分布式交易;跨不同数据库的事务可能在不同系统上。这需要在操作系统和SQL Server上进行一些配置,如果没有这些配置将无法使用。如果所有查询都针对单个数据库实例,则不建议这样做。
    但是,对于单个数据库,当您需要将代码包含在不受您控制的事务中时,这可能会很有用。对于单个数据库,它也不需要特殊的配置。

  2. connection.BeginTransaction是ADO.NET语法,用于针对单个数据库实现事务(在C#,VB.NET等中)。这不适用于多个数据库。

所以, connection.BeginTransaction()是更好的方法。

处理事务的更好方法是实现答案中解释的UnitOfWork 。


4
一个不需要多个数据库即可从TransactionScope中受益。特别有用的是环境。非常适合在事务中包装您不拥有或不能修改的代码。例如,当执行数据库的单元/集成测试代码调用之后要回滚的地方时,它可以发挥很大的作用。只需浮动一个TransactionScope,测试代码,然后在测试清理期间进行处理即可。
拉里·史密斯

3
@LarrySmith:同意;但问题不在于此。OP只是说他想在一个事务中插入多个表。包括已接受的一些答案在内,建议使用TransactionScope对于OP所需的效率低下的答案。我同意TransactionScope在许多情况下这是个好工具;但不是这个。
阿米特·乔希

5

丹尼尔的答案符合我的预期。为了完整起见,下面的代码片段演示了如何使用事务作用域和dapper进行提交和回滚:

using System.Transactions;
    // _sqlConnection has been opened elsewhere in preceeding code 
    using (var transactionScope = new TransactionScope())
    {
        try
        {
            long result = _sqlConnection.ExecuteScalar<long>(sqlString, new {Param1 = 1, Param2 = "string"});

            transactionScope.Complete();
        }
        catch (Exception exception)
        {
            // Logger initialized elsewhere in code
            _logger.Error(exception, $"Error encountered whilst executing  SQL: {sqlString}, Message: {exception.Message}")

            // re-throw to let the caller know
            throw;
        }
    } // This is where Dispose is called 

2
@usr取决于个人喜好。我更喜欢第一次出现问题,并且不会将日志语句视为垃圾。另外,我的答案仍然是通过演示一种使用
短小巧的

@CodeNaked,首先,您在此输入的命令有误。如果有异常,将首先命中catch块,然后终止使用范围。其次,查看此答案和参考的MSDN文档:stackoverflow.com/a/5306896/190476调用第二次处置并没有害处,一个设计良好的对象将忽略第二次调用。否决票是没有道理的!
Sudhanshu Mishra'8

@dotnetguy-我并不是试图交流哪个Dispose方法被称为第一个或第二个方法,只是它被调用了两次。关于“第二次呼叫处置无害”这一点,这是一个很大的假设。我了解到,文档和实际的实现常常会不一致。但是,如果您想要微软的话: msdn.microsoft.com/en-us/library/...
CodeNaked

3
那么,代码分析警告是您不赞成投票的理由吗?那不会使答案错误或误导-那就是适当的否决票。为什么不编辑答案并在保留功能的同时提出更好的解决方案?堆栈溢出是有关帮助和建设性批评的全部。
Sudhanshu Mishra
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.