Как использовать транзакции с dapper.net?

Я хотел бы запустить несколько операторов вставки в нескольких таблицах. Я использую dapper.net. Я не вижу способа обрабатывать транзакции с dapper.net.

Поделитесь своими идеями о том, как использовать транзакции с dapper.net.


person Amit    schedule 28.04.2012    source источник


Ответы (6)


Вот фрагмент кода:

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

Обратите внимание, что вам нужно добавить ссылку на сборку System.Transactions, потому что по умолчанию на нее нет ссылки.

person the_joric    schedule 28.04.2012
comment
Требуется ли явный откат при ошибке или System.Transactions обрабатывает это автоматически? - person Norbert Norbertson; 03.10.2017
comment
@NorbertNorbertson делает это автоматически, методом Dispose(). Если Complete() не был вызван, транзакция откатывается. - person the_joric; 04.10.2017
comment
Стоит упомянуть из-за другого ответа (stackoverflow.com/a/20047975/47672): соединение должно быть открыто внутри TransctionScope используя блок, если вы выберете этот ответ. - person 0x49D1; 08.06.2018
comment
См. Также (stackoverflow.com/a/20047975/444469) - Требуется DoYouDapperWork (выполнение, запрос и т. Д.) сделка в параметрах. - person Matthieu; 18.07.2018
comment
Откат вызывается автоматически при возникновении проблемы? - person gandalf; 08.03.2019
comment
@gandalf Да, это так. Если Complete () не вызывается, откатывается вся транзакция. - person James Poulose; 23.03.2020
comment
Работает ли это, если ваш DoYourDapperWork () использует несколько SqlConnections для выполнения своей работы? Например, допустим, у меня есть репозиторий dapper, в котором каждый метод использует новое соединение. Могу ли я назвать несколько из них TransactionScope? - person Ε Г И І И О; 23.05.2021

Я предпочел использовать более интуитивный подход, получая транзакцию непосредственно из соединения:

// 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();
}
person ANeves thinks SE is evil    schedule 18.11.2013
comment
@ANeves: Что ж, мы, вероятно, используем разные фреймворки Dapper, потому что у этого есть: github.com / StackExchange / dapper-dot-net - person andrecarlucci; 28.11.2014
comment
необходимо вызвать connection.open () перед .begintransaction - person Timeless; 05.06.2015
comment
Соединение не включается автоматически в область транзакций, если вы не открываете соединение в области транзакции. Я не знаю, как работает ваш код, если GetOpenConnection каким-то волшебным образом открывается в области транзакций, но держу пари, что это не так. - person Erik Bergstedt; 22.11.2015
comment
@ErikBergstedt, вы говорите, что соединение должно открываться только после, которое мы вызываем .BeginTransaction()? Если бы это было так, этот метод расширения способствовал бы неправильному использованию транзакции. (IMO, он должен даже бросить не удается открыть транзакцию после того, как соединение уже открыто.) - person ANeves thinks SE is evil; 23.11.2015
comment
Хороший момент для включения транзакции в качестве параметра в Execute, так как это необходимо. - person Arve Systad; 14.06.2017

Вы должны иметь возможность использовать TransactionScope, поскольку Dapper выполняет только команды ADO.NET.

using (var scope = new TransactionScope())
{
   // open connection
   // insert
   // insert
   scope.Complete();
}
person Daniel A. White    schedule 28.04.2012

Учитывая, что все ваши таблицы находятся в единой базе данных, я не согласен с решением TransactionScope, предложенным в некоторых ответах здесь. См. этот ответ.

  1. TransactionScope обычно используется для распределенных транзакций; транзакция, охватывающая разные базы данных, может находиться в другой системе. Для этого требуются некоторые настройки операционной системы и SQL Server, без которых это не будет работать. Это не рекомендуется, если все ваши запросы относятся к одному экземпляру базы данных.
    Но с одной базой данных это может быть полезно, когда вам нужно включить код в транзакцию, которая не находится под вашим контролем. При единой базе данных не требуется особых настроек.

  2. connection.BeginTransaction - это синтаксис ADO.NET для реализации транзакции (в C #, VB.NET и т. Д.) С единой базой данных. Это не работает с несколькими базами данных.

Итак, connection.BeginTransaction() - лучший способ пойти.

Даже лучший способ обработки транзакции - это реализовать UnitOfWork, как описано в этом ответе.

person Amit Joshi    schedule 07.11.2017
comment
Чтобы воспользоваться TransactionScope, не нужно несколько баз данных. Особая полезность в том, что это эмбиент. Он отлично подходит для обертывания кода, который вам не принадлежит или который вы не можете изменить, в транзакцию. Например, его можно использовать для большого эффекта, когда код модульного / интеграционного тестирования выполняет вызовы базы данных, после чего вы хотите выполнить откат. Просто разместите TransactionScope, протестируйте код и удалите его во время тестовой очистки. - person Larry Smith; 15.11.2017
comment
@LarrySmith: Согласен; но вопрос не ни в чём. OP просто говорит, что хочет вставить в несколько таблиц за одну транзакцию. В некоторых ответах, включая принятый, предлагается использовать TransactionScope, что неэффективно для того, что хочет OP. Я согласен, что TransactionScope - хороший инструмент во многих случаях; но не это. - person Amit Joshi; 15.11.2017

Ответ Дэниела сработал для меня, как и ожидалось. Для полноты, вот фрагмент, который демонстрирует фиксацию и откат с использованием области транзакции и 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 
person Sudhanshu Mishra    schedule 01.12.2015
comment
@usr зависит от личных предпочтений. Я предпочитаю знать, когда что-то пошло не так в первый раз, и не воспринимаю записи журнала как мусор. Кроме того, мой ответ по-прежнему имеет ценность рекламы, демонстрируя один из способов использования транзакций с dapper. - person Sudhanshu Mishra; 02.12.2015
comment
@CodeNaked, во-первых, у вас неправильный порядок. Если есть исключение, сначала будет обработан блок catch, а затем - конец области действия. Во-вторых, посмотрите на этот ответ и указанный документ MSDN: stackoverflow.com/a/5306896/190476, вызывая удаление во второй раз не вредно, хорошо спроектированный объект игнорирует второй вызов. Голос против не обоснован! - person Sudhanshu Mishra; 23.08.2016
comment
@dotnetguy - я не пытался сообщить, какой метод Dispose вызывается первым или вторым, просто он вызывается дважды. Что касается того, что повторный вызов dispose не вреден, это большое предположение. Я узнал, что документы и фактические реализации часто не совпадают. Но если вам нужны слова Microsoft: msdn. microsoft.com/en-us/library/ - person CodeNaked; 24.08.2016
comment
Итак, предупреждение анализа кода - ваша причина проголосовать против? Это не делает ответ неправильным или вводящим в заблуждение - вот тогда уместно отрицательное голосование. Почему бы вам не отредактировать ответ и не предложить лучшее решение, сохранив при этом функциональность? Stack overflow - это помощь и конструктивная критика. - person Sudhanshu Mishra; 24.08.2016

В Dapper есть 3 подхода к выполнению транзакций.

  1. Простая транзакция
  2. Транзакция из области транзакции
  3. Использование Dapper Transaction (дополнительный пакет nuget и наиболее предпочтительный подход )

Вы можете узнать больше об этих подходах к транзакциям на официальном веб-сайте руководства здесь

Для справки вот разбивка подходов к транзакциям

1. Простая транзакция

В этом примере вы создаете транзакцию в существующем соединении с базой данных, а затем передаете транзакцию методу Execute на dapper (который является необязательным параметром).

Выполнив всю свою работу, просто зафиксируйте транзакцию.

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. Транзакция из области транзакции

Если вы хотите создать область транзакции, вам нужно будет сделать это до создания соединения с базой данных. После того, как вы создали область транзакции, вы можете просто выполнить все свои операции, а затем выполнить один вызов для завершения транзакции, которая затем зафиксирует все команды.

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. Использование Dapper Transaction

Это наиболее благоприятный подход для выполнения транзакции в коде, поскольку он упрощает чтение и реализацию кода. Существует расширенная реализация транзакции SQL под названием Dapper Transaction (которую вы можете найти здесь ), что позволяет запускать 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();
    }
}
person Newteq Developer    schedule 10.05.2021