Рефакторинг на 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, дори ако те сочат към една и съща DB.

person RickNZ    schedule 18.11.2009

Microsoft препоръчва използването на обхват на транзакция:

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

Основната идея е, че обхватът на транзакцията ще управлява „околния контекст на транзакция“ вместо вас. Започвате, като говорите с една база данни, имате sql транзакция, след това говорите с база данни номер 2 и транзакцията се издига до разпределена транзакция.

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

РЕДАКТИРАНЕ

Когато използвате обхват на транзакция, всичко в този обхват се покрива от транзакцията. Следователно запазвате ред код, където свързвате командата с транзакцията. Това е възможен източник на грешка, например, ако имаше един шанс на 1000 този ред да е бил забравен, колко ще липсвате.

РЕДАКТИРАНЕ 2

Съгласен съм с коментара за Triynko по-долу. Ние обаче използваме 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, защитна стена и т.н. Затова препоръчах използването на SqlTransaction е по-спестяващо изпълнение.

person Wawan    schedule 18.11.2009

Добре, може би е твърде късно за това... но както и да е, ще го напиша за интересуващите се...

Тъй като сега имам по-добра картина, след като имах много трудности с текущия ми подход, базиран на SqlTransaction, който мога да променя в полза на TransactionScope, както виждам... основното предимство на TransactionScope е, че може да се използва много лесно в Business Layer.

person Learner    schedule 09.06.2011

Също така късно... Можете лесно да имате „вложени“ транзакции в бизнес слоя, дори ако базата данни не поддържа вложени транзакции. .NET контролира влагането и в крайна сметка използва една транзакция на база данни (поне в случая на SQL Server 2008+). Това прави много по-лесно повторното използване на код за достъп до данни извън първоначалното му предназначение, като част от по-голяма транзакция.

person Nelson Rothermel    schedule 03.04.2014