Рефакторинг ADO.NET - SqlTransaction против TransactionScope

Я унаследовал небольшой метод C #, который создает объект ADO.NET SqlCommand и перебирает список элементов, которые нужно сохранить в базе данных (SQL Server 2005).

Прямо сейчас используется традиционный подход SqlConnection / SqlCommand, и чтобы убедиться, что все работает, два шага (удаление старых записей, затем вставка новых) заключены в ADO.NET SqlTransaction.

using (SqlConnection _con = new SqlConnection(_connectionString))
{
   using (SqlTransaction _tran = _con.BeginTransaction())
   {
      try
      {
         SqlCommand _deleteOld = new SqlCommand(......., _con);
         _deleteOld.Transaction = _tran;
         _deleteOld.Parameters.AddWithValue("@ID", 5);

         _con.Open();

         _deleteOld.ExecuteNonQuery();

         SqlCommand _insertCmd = new SqlCommand(......, _con);
         _insertCmd.Transaction = _tran;

         // add parameters to _insertCmd

         foreach (Item item in listOfItem)
         {
            _insertCmd.ExecuteNonQuery();
         }

         _tran.Commit();
         _con.Close();
       }
       catch (Exception ex)
       {
          // log exception
          _tran.Rollback();
          throw;
       }
    }
}

В последнее время я много читал о классе .NET TransactionScope, и мне было интересно, какой подход здесь предпочтительнее? Получу ли я что-нибудь (читаемость, скорость, надежность), переключившись на использование

using (TransactionScope _scope = new TransactionScope())
{
  using (SqlConnection _con = new SqlConnection(_connectionString))
  {
    ....
  }

  _scope.Complete();
}

Что бы вы предпочли и почему?

Марк


person marc_s    schedule 13.08.2009    source источник


Ответы (6)


Вы ничего не получите немедленно, переключив существующий код на использование TransactionScope. Вы должны использовать его для будущих разработок из-за гибкости, которую он обеспечивает. В будущем это упростит включение в транзакцию других вещей, кроме вызовов ADO.NET.

Кстати, в вашем опубликованном примере экземпляры SqlCommand должны быть в using блоках.

person John Saunders    schedule 13.08.2009
comment
Хорошо, спасибо, Джон - и да, вы правы - в этом примере нет SqlCommand в блоках using () (пока!) - это работа :-) - person marc_s; 13.08.2009
comment
re: SQLCommand внутри использования, есть ли какая-либо другая причина, помимо сборки мусора, например: Dispose () для SQLConnection вызывает метод Close (), поэтому Dispose () вызывает какие-либо методы SQLCommand? - person Troy DeMonbreun; 20.08.2009
comment
Если класс реализует IDisposable, и если вы создаете экземпляр этого класса, вы должны вызвать для него Dispose. Самый простой способ сделать это - использовать блок using. Я считаю, что лучше выработать привычку всегда реализовывать одну из них. - person John Saunders; 20.08.2009

Я предпочитаю TransactionScope. Он не работает идеально в каждом сценарии, но в том, который вы описываете, это лучшее решение.

Мои рассуждения:

  1. Включение в транзакцию происходит автоматически
  2. Откат транзакции в случае возникновения исключения - автоматический

В совокупности в результате получается немного меньше кода и в целом более надежная конструкция, поскольку система обрабатывает некоторые детали за меня; мне нужно помнить на одну вещь меньше.

Кроме того, прозрачная регистрация транзакций может быть особенно полезна, когда у вас есть несколько вложенных методов в вашем DAL - хотя вы должны позаботиться о том, чтобы ваша транзакция случайно не превратилась в распределенную, для которой требуется DTC, что может произойти, если вы используете несколько SqlConnections, даже если они указывают на одну и ту же БД.

person RickNZ    schedule 18.11.2009

Microsoft рекомендует использовать область транзакции:

http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx

Основная идея состоит в том, что область транзакции будет управлять «внешним контекстом транзакции» за вас. Вы начинаете с разговора с одной базой данных, у вас есть транзакция sql, затем вы разговариваете с базой данных номер 2, и транзакция повышается до распределенной транзакции.

Объем транзакции действительно работает на вас, так что вы можете сосредоточиться на функциональности системы, а не на сантехнике.

ИЗМЕНИТЬ

Когда вы используете область транзакции, все, что находится в этой области, покрывается транзакцией. Поэтому вы сохраняете строку кода, в которой подключаете команду к транзакции. Это возможный источник ошибки, например, если бы был один шанс из 1000, что эта строка была пропущена, сколько вы бы пропустили.

ИЗМЕНИТЬ 2

Согласен с комментарием к Трийнко ниже. Однако мы используем Entity Framework, EF автоматически закроет и снова откроет соединение, чтобы подключить его к транзакции. Он не закрывает соединение физически, скорее, он выпускает его в пул соединений и получает новый, который может быть таким же или другим.

person Shiraz Bhaiji    schedule 13.08.2009
comment
В ПОРЯДКЕ,. Спасибо за это. Но получу ли я хоть какую-то выгоду, если я реорганизую все (не только этот пример), чтобы использовать TransactionScope () вместо встроенных транзакций ADO.NET? Просто спросите, стоит ли оно того - что я от этого выиграю? - person marc_s; 13.08.2009
comment
Когда вы используете область транзакции, все, что находится в этой области, покрывается транзакцией. Нет, не все покрывается. Только команды, выпущенные для соединений, перечисленных в области действия, затрагиваются областью действия. Соединения автоматически включаются в область видимости, если они открыты в области, в противном случае уже открытые соединения необходимо вручную включить в область после ее создания путем вызова SqlConnection.EnlistTransaction. Если вы, например, откроете свое соединение, а затем создадите область транзакции ... ни одна из ваших команд не будет задействована в транзакции. - person Triynko; 22.05.2010
comment
@Triynko: Ваш комментарий к соответствующей статье MSDN великолепен! - person abatishchev; 15.07.2010

Просто обратите внимание, что с использованием Transaction Scope иногда возникают большие проблемы, потому что многие настройки, которые мы должны выполнить на сервере, такие как настройка DTC, Firewall и т. Д., Поэтому я рекомендовал использовать SqlTransaction, более экономичный в реализации.

person Wawan    schedule 18.11.2009

Ладно, может, уже поздно ... Но в любом случае запишу для желающих ...

Поскольку теперь у меня есть лучшая картина, после множества трудностей с моим текущим подходом, основанным на SqlTransaction, который я мог бы изменить в пользу TransactionScope, как я это вижу ... главное преимущество TransactionScope в том, что его можно использовать очень легко на бизнес-уровне.

person Learner    schedule 09.06.2011

Также поздно ... Вы можете легко иметь «вложенные» транзакции на бизнес-уровне, даже если база данных не поддерживает вложенные транзакции. .NET контролирует вложение и в конечном итоге использует одну транзакцию базы данных (по крайней мере, в случае SQL Server 2008+). Это значительно упрощает повторное использование кода доступа к данным вне его первоначального назначения в рамках более крупной транзакции.

person Nelson Rothermel    schedule 03.04.2014