Транзакции в .net

144

Каковы лучшие практики для выполнения транзакций в C # .Net 2.0. Какие классы следует использовать? На какие подводные камни нужно обратить внимание и т. Д. Все, что фиксирует и откатывает вещи. Я только начинаю проект, в котором мне может потребоваться выполнить некоторые транзакции при вставке данных в БД. Любые ответы или ссылки даже на базовые сведения о транзакциях приветствуются.

Малик Дауд Ахмад Хохар
источник
Вот хороший пример Транзакций в .NET на codeproject для использования в качестве начала.
Митчел Селлерс

Ответы:

271

Есть 2 основных вида транзакций; транзакции соединения и окружающие транзакции. Транзакция соединения (например, SqlTransaction) напрямую связана с соединением db (например, SqlConnection), что означает, что вам нужно постоянно передавать соединение - ОК в некоторых случаях, но не разрешать «создавать / использовать / освобождать» использование, и не позволяет кросс-дБ работать. Пример (отформатированный для пробела):

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

Не слишком грязно, но ограничено нашей связью "Конн". Если мы хотим вызвать разные методы, нам нужно передать «conn».

Альтернатива - внешняя транзакция; Впервые в .NET 2.0 объект TransactionScope (System.Transactions.dll) позволяет использовать различные операции (подходящие поставщики автоматически подключаются к внешней транзакции). Это позволяет легко встраиваться в существующий (не транзакционный) код и общаться с несколькими провайдерами (хотя DTC будет участвовать, если вы говорите с более чем одним).

Например:

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

Обратите внимание, что эти два метода могут обрабатывать свои собственные соединения (открывать / использовать / закрывать / удалять), но они будут тихо становиться частью транзакции окружения без необходимости что-либо передавать.

Если ваш код ошибки, Dispose () будет вызван без Complete (), поэтому он будет откат. Поддерживается ожидаемое вложение и т. Д., Хотя вы не можете откатить внутреннюю транзакцию, но завершить внешнюю транзакцию: если кто-то недоволен, транзакция отменяется.

Другое преимущество TransactionScope заключается в том, что он не привязан только к базам данных; любой провайдер, поддерживающий транзакции, может использовать его. WCF, например. Или есть даже некоторые TransactionScope-совместимые объектные модели (например, классы .NET с возможностью отката - возможно, проще, чем сувенир, хотя я никогда не использовал этот подход сам).

В общем, очень, очень полезный объект.

Некоторые предостережения:

  • В SQL Server 2000 TransactionScope немедленно перейдет в DTC; это исправлено в SQL Server 2005 и выше, он может использовать LTM (намного меньше накладных расходов), пока вы не поговорите с 2 источниками и т. д., когда он повышен до 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

если вам это просто нужно для вещей, связанных с БД, некоторые OR 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и транзакция всегда будет фиксироваться или откатываться (в зависимости от того, что вы скажете). Самая большая проблема, с которой мы столкнулись, заключалась в том, чтобы убедиться, что она всегда совершена. Использование гарантирует, что объем сделки ограничен.

Броуди
источник