Операції в .net


144

Які найкращі практики робити транзакції в C # .Net 2.0. Які класи потрібно використовувати? На які підводні камені слід звернути увагу і т.д. Я тільки починаю проект, де мені може знадобитися зробити деякі транзакції, вставляючи дані в БД. Будь-які відповіді або посилання на навіть основні речі щодо транзакцій вітаються.


Ось хороший приклад транзакцій у .NET із кодового проекту для використання в якості запуску.
Продавці Мітчела

Відповіді:


271

Існує 2 основних види операцій; транзакції з підключенням та зовнішні транзакції. Трансакція підключення (наприклад, SqlTransaction) прив’язана безпосередньо до db-з'єднання (наприклад, SqlConnection), що означає, що вам доведеться продовжувати передачу з'єднання навколо - OK у деяких випадках, але не дозволяє "створювати / використовувати / випускати" використання та не дозволяє працювати з перехресними db. Приклад (відформатований для простору):

using (IDbTransaction tran = conn.BeginTransaction()) {
    try {
        // your code
        tran.Commit();
    }  catch {
        tran.Rollback();
        throw;
    }
}

Не надто безладний, але обмежений нашим зв'язком "conn". Якщо ми хочемо закликати до різних методів, тепер нам потрібно пройти "conn" навколо.

Альтернатива - зовнішня транзакція; Новий в .NET 2.0 об'єкт TransactionScope (System.Transaction.dll) дозволяє використовувати для ряду операцій (відповідні провайдери автоматично зараховуються до зовнішньої транзакції). Це дозволяє легко ретро вписатись у існуючий (не транзакційний) код та поговорити з декількома постачальниками (хоча DTC буде залучатися, якщо ви поговорите з більш ніж одним).

Наприклад:

using(TransactionScope tran = new TransactionScope()) {
    CallAMethodThatDoesSomeWork();
    CallAMethodThatDoesSomeMoreWork();
    tran.Complete();
}

Зауважте тут, що два способи можуть обробляти свої власні з'єднання (відкривати / використовувати / закривати / розпоряджатися), але вони мовчки стануть частиною зовнішньої транзакції, без того, щоб нам нічого проходити.

Якщо у вашому коді помилки, Dispose () буде викликано без завершення (), тому він буде повернутий назад. Очікується, що очікується гніздування тощо. Підтримується, хоча ви не можете відкатати внутрішню транзакцію, але зовнішню транзакцію не завершено: якщо хтось незадоволений, транзакція припиняється.

Інша перевага TransactionScope полягає в тому, що він не прив’язаний лише до баз даних; будь-який постачальник, обізнаний з транзакціями, може використовувати його. WCF, наприклад. Або є навіть деякі об'єктні моделі TransactionScope (тобто .NET-класи з можливістю відкату - можливо, простіше, ніж пам'ять, хоча я ніколи не використовував такий підхід).

Загалом, дуже, дуже корисний об’єкт.

Деякі застереження:

  • На SQL Server 2000 TransactionScope негайно перейде до DTC; це виправлено в SQL Server 2005 і вище, він може використовувати LTM (набагато менше накладних витрат), поки ви не поговорите з двома джерелами тощо, коли він буде підвищений до DTC.
  • Існує глюк, який означає, що вам може знадобитися підключити рядок з'єднання

CSLA .NET 2.0 підтримує об’єкт TransactionScope!
Біной Антоній

Проблема тут полягає в тому, коли у вас є транзакція в першому методі, і цей метод (інкапсуляція) не знає, буде викликано з батьківської транзакції чи ні.
Едуардо Молтені

1
@Eduardo - це не проблема при використанні TransactionScope, що робить його дуже привабливим. Такі угоди гніздяться, і здійснюються лише найвіддаленіші.
Марк Гравелл

Я сподіваюся, що ви все ще слухаєте. Ви сказали, що навколо є "деякі об'єкти, сумісні з TransactionScope". Чи можете ви вказати мені на деякі з них? Дякую.
majkinetor

1
Знову Марк, ще одне відмінне пояснення. Коли ви говорите, що "очікуване введення підтримується", це для блоків транзакцій, визначених в самих методах (наприклад, CallAMethodThatDoesSomeWork ())? Або, якщо зовнішній запис транзакцій визначений зовні, це не потрібно?
Філ Купер

11
protected void Button1_Click(object sender, EventArgs e)
   {


       using (SqlConnection connection1 = new SqlConnection("Data Source=.\\SQLEXPRESS;AttachDbFilename=|DataDirectory|\\Database.mdf;Integrated Security=True;User Instance=True"))
       {
           connection1.Open();

           // Start a local transaction.
           SqlTransaction sqlTran = connection1.BeginTransaction();

           // Enlist a command in the current transaction.
           SqlCommand command = connection1.CreateCommand();
           command.Transaction = sqlTran;

           try
           {
               // Execute two separate commands.
               command.CommandText =
                "insert into [doctor](drname,drspecialization,drday) values ('a','b','c')";
               command.ExecuteNonQuery();
               command.CommandText =
                "insert into [doctor](drname,drspecialization,drday) values ('x','y','z')";
               command.ExecuteNonQuery();

               // Commit the transaction.
               sqlTran.Commit();
               Label3.Text = "Both records were written to database.";
           }
           catch (Exception ex)
           {
               // Handle the exception if the transaction fails to commit.
               Label4.Text = ex.Message;


               try
               {
                   // Attempt to roll back the transaction.
                   sqlTran.Rollback();
               }
               catch (Exception exRollback)
               {
                   // Throws an InvalidOperationException if the connection 
                   // is closed or the transaction has already been rolled 
                   // back on the server.
                   Label5.Text = exRollback.Message;

               }
           }
       }


   }

4

Ви також можете обернути транзакцію у свою власну збережену процедуру та обробити її таким чином, а не робити транзакції в самій C #.


1

якщо вам просто потрібні речі, пов'язані з db, деякі АБО Mappers (наприклад, NHibernate) підтримують трансактіно з коробки за замовчуванням.


0

Це також залежить від того, що вам потрібно. Для базових транзакцій SQL ви можете спробувати робити транзакції TSQL, використовуючи BEGIN TRANS та COMMIT TRANS у своєму коді. Це найпростіший спосіб, але він має складність, і ви повинні бути обережними, щоб правильно здійснити (і відкатати).

Я б використав щось подібне

SQLTransaction trans = null;
using(trans = new SqlTransaction)
{
    ...
    Do SQL stuff here passing my trans into my various SQL executers
    ...
    trans.Commit  // May not be quite right
}

Будь-який збій вискочить з місця, usingі транзакція завжди буде здійснена або відкатна (залежно від того, що ви скажете). Найбільша проблема, з якою ми стикалися, - це переконатися, що вона завжди вчиняється. Використання гарантує обмеження обсягу транзакції.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.