C# 如何使用dapper.net的事务?

C# 如何使用dapper.net的事务?,c#,transactions,dapper,C#,Transactions,Dapper,我想在多个表上运行多个insert语句。我正在使用dapper.net。我看不出用dapper.net处理事务的任何方法 请分享您关于如何使用dapper.net的事务的想法。您应该能够使用TransactionScope,因为dapper只运行ADO.net命令 using (var scope = new TransactionScope()) { // open connection // insert // insert scope.Complete(); }

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


请分享您关于如何使用dapper.net的事务的想法。

您应该能够使用
TransactionScope
,因为dapper只运行ADO.net命令

using (var scope = new TransactionScope())
{
   // open connection
   // insert
   // insert
   scope.Complete();
}
下面是代码片段:

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

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

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

// 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();
}

丹尼尔的回答对我来说正如预期的那样有效。为了完整起见,下面是一个演示使用事务作用域和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 
使用系统事务;
//\u sqlConnection已在前面代码中的其他位置打开
使用(var transactionScope=new transactionScope())
{
尝试
{
long result=_sqlConnection.ExecuteScalar(sqlString,new{Param1=1,Param2=“string”});
transactionScope.Complete();
}
捕获(异常)
{
//记录器在代码中的其他地方初始化
_logger.Error(异常,$“执行SQL时遇到错误:{sqlString},消息:{exception.Message}”)
//重新抛出,让来电者知道
投掷;
}
}//这是调用Dispose的地方

考虑到您所有的表都在一个数据库中,我不同意这里的一些答案中建议的
TransactionScope
解决方案。参考答案

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

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

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


    甚至更好的处理事务的方法是实现UnitOfWork,如回答中所述。

    有3种方法可以简洁地处理事务

  • 简单事务
  • 事务范围中的事务
  • 使用简洁的交易(和最优惠方法
  • 你可以在官方教程网站上找到更多关于这些交易方法的信息

    以下是交易方法的分类,以供参考

    1。简单交易

    在本例中,您在现有数据库连接上创建一个事务,然后将该事务传递给dapper上的Execute方法(这是一个可选参数)

    完成所有工作后,只需提交事务即可

    string sql = "INSERT INTO Customers (CustomerName) Values (@CustomerName);";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {
        connection.Open();
        
        using (var transaction = connection.BeginTransaction())
        {
            connection.Execute(sql, new {CustomerName = "Mark"}, transaction: transaction);
            connection.Execute(sql, new {CustomerName = "Sam"}, transaction: transaction);
            connection.Execute(sql, new {CustomerName = "John"}, transaction: transaction);
            
            transaction.Commit();
        }
    }
    
    2。来自交易范围的交易

    如果要创建事务作用域,则需要在创建db连接之前执行此操作。一旦创建了事务作用域,就可以简单地执行所有操作,然后执行一个调用来完成事务,然后提交所有命令

    using (var transaction = new TransactionScope())
    {
        var sql = "INSERT INTO Customers (CustomerName) Values (@CustomerName);";
    
        using (var connection = My.ConnectionFactory())
        {
            connection.Open();
    
            connection.Execute(sql, new {CustomerName = "Mark"});
            connection.Execute(sql, new {CustomerName = "Sam"});
            connection.Execute(sql, new {CustomerName = "John"});
        }
    
        transaction.Complete();
    }
    
    3。使用简洁的事务处理

    这是在代码中实现事务的最有利方法,因为它使代码易于阅读和实现。有一个SQL事务的扩展实现,称为Dapper事务(您可以找到),它允许您直接在事务之外运行SQL执行

    string sql = "INSERT INTO Customers (CustomerName) Values (@CustomerName);";
    
    using (var connection = new SqlConnection(FiddleHelper.GetConnectionStringSqlServerW3Schools()))
    {
        connection.Open();
        
        using (var transaction = connection.BeginTransaction())
        {
            transaction.Execute(sql, new {CustomerName = "Mark"});
            transaction.Execute(sql, new {CustomerName = "Sam"});
            transaction.Execute(sql, new {CustomerName = "John"});
    
            transaction.Commit();
        }
    }
    

    @ANeves:嗯,我们可能使用不同的Dapper框架,因为这个框架有:必须在.beginTransaction之前调用connection.open()。除非在transactionscope中打开连接,否则连接不会自动登记到transactionscope中。我不知道你的代码是如何工作的,如果GetOpenConnection在transactionscope中以某种方式神奇地打开自己,但我敢打赌它不会t@ErikBergstedt,您是说只有在我们调用
    .BeginTransaction()
    后,连接才能打开吗?如果是这样的话,这种扩展方法将促进事务的错误使用。(依我看,它甚至应该抛出“连接已打开后无法打开事务”。)最好将事务作为参数包含在
    Execute
    中,因为这是必需的。@usr这取决于个人偏好。我更喜欢知道第一次出错的时候,不要把日志语句看作垃圾。此外,我的回答还通过演示一种使用事务的方法来宣传价值dapper@CodeNaked首先,你的订单错了。如果出现异常,将首先命中catch块,然后到达使用范围的末尾。第二,看看这个答案和引用的MSDN文档:第二次调用dispose是无害的,设计良好的对象会忽略第二次调用。否决票是没有道理的@dotnetguy-我并没有试图传达第一次或第二次调用哪个
    Dispose
    方法,只是调用了两次。至于“第二次调用dispose没有害处”这一点,这是一个很大的假设。我了解到文档和实际实现往往不一致。但如果你想要微软的话:那么,代码分析警告是你否决投票的理由吗?这并不意味着答案是错误的或是误导性的——这是投反对票的时候了。为什么不编辑答案并提出更好的解决方案,同时保留功能?堆栈溢出是关于帮助和建设性的批评。它是必要的吗