Как преобразовать в транзакции ADO.NET, а не транзакции SQL Server? - PullRequest
2 голосов
/ 01 февраля 2010

Прямо сейчас у меня есть код, который инициирует транзакции на SQL Server, используя предполагаемый метод:

ExecuteNonQuery(connection, "BEGIN TRANSACTION");
try
{
   DoABunchOnStuff(connection);
   DoSomeMoreStuff(connection);
   JustAFewMoreThings(connection);

   ExecuteNonQuery(connection, "COMMIT TRANSACTION");
} 
catch (Exception)
{  
   ExecuteNonQuery(connection, "ROLLBACK TRANSACTION");
   throw;
}

Теперь я думаю о возможности изучения идеи использования абстракции транзакций, предоставленной ADO.NET:

.
DbTransaction trans = connection.BeginTransaction();
try
{
   DoABunchOnStuff(connection);
   DoSomeMoreStuff(connection);
   JustAFewMoreThings(connection);

   trans.Commit();
} 
catch (Exception)
{  
   trans.Rollback();
   throw;
}

Проблема с этим простым преобразованием транзакций на основе SQL Server в транзакции ADO.NET заключается в ошибке:

ExecuteNonQuery требует команды иметь сделку, когда соединение, назначенное команде в ожидающей локальной транзакции. Свойство транзакции команды не был инициализирован.

Правильно ли я предположил, что если бы я хотел использовать транзакции ADO.NET, мне пришлось бы полностью уничтожить инфраструктуру, передав объект DbTransaction каждому методу, который работает или может работать внутри сделка?

Ответы [ 3 ]

5 голосов
/ 01 февраля 2010

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

Пример:

using (TransactionScope tsc = new TransactionScope())
{
    DoABunchOnStuff(connection);
    DoSomeMoreStuff(connection);
    JustAFewMoreThings(connection);
    tsc.Complete();
}

Примечания по использованию TransactionScope:

  • Вы должны обязательно включить Transaction Binding = Explicit Unbind в строку подключения. По умолчанию транзакции выполняются в режиме неявного снятия привязки, что означает, что они переключаются в режим автоматической фиксации, если время транзакции истекло. Вы почти никогда не хотите поведение по умолчанию, так как оно может помешать атомарности ваших транзакций и вызвать то, что некоторые люди называют повреждением данных (даже если это не фактическое «повреждение»). Пока вы используете правильные параметры в строке подключения, вам не нужно беспокоиться об этом.

  • TransactionScope будет повышен до DTC (распределенная транзакция), если в области имеется более одного подключения, которое включает связанные серверы и OPENROWSET. Хотя это может показаться нежелательным поведением, ваш код не будет безопасным с другой стороны. Выполнение инструкций BEGIN TRAN вручную для нескольких соединений и помещение нескольких операторов ROLLBACK в обработчик исключений не обеспечивает атомарность всей транзакции.

  • Области транзакции предназначены для вложения и автоматически вычисляют разницу между началом новой транзакции и зачислением в существующую. Это намного мощнее, чем сопоставление операторов BEGIN TRAN и COMMIT / ROLLBACK, поскольку последние полагаются на число локальных транзакций соединения, тогда как первый на самом деле ... scoped . Использование TransactionScope аналогично обработке структурированных транзакций в SQL Server с использованием SAVE TRAN, TRY / CATCH и именем ROLLBACK - вам не нужно беспокоиться о том, что произойдет, если последующий процесс или процедура отбрасывает транзакцию логика, которая представляет серьезный риск при отправке необработанных операторов BEGIN и ROLLBACK через ADO.NET.

3 голосов
/ 01 февраля 2010

Правильно ли я считаю, что если я хотел использовать транзакции ADO.NET я придется полностью потрошить инфраструктура, проходя вдоль Объект DbTransaction для каждого метода который работает или может работать внутри сделка?

Да, именно - вам нужно связать созданную вами транзакцию с каждой SqlCommand, которая должна выполняться под эгидой этой транзакции, поэтому вам нужно иметь что-то вроде:

DbTransaction trans = connection.BeginTransaction();
try
{
   DoABunchOnStuff(connection, trans);
   DoSomeMoreStuff(connection, trans);
   JustAFewMoreThings(connection, trans);

   trans.Commit();
} 
catch (Exception)
{  
   trans.Rollback();
   throw;
}

и внутри этих методов что-то вроде:

public void DoABunchOnStuff(SqlConnection connection, SqlTransaction trans)
{
    using(SqlCommand cmd = new SqlCommand(--sql stmt--, connection, trans)
    {
       ........
    } 
} 
0 голосов
/ 01 февраля 2010

Вы также можете взглянуть на Linq to SQL. Так же вы можете также «SubmitChanges ()» (или не отправлять их) в коде в базу данных. Это означает, что вы можете обернуть его в попытку, как в вашей транзакции. Это тоже небольшое изменение инфраструктуры, но с SQLMetal вы можете автоматически генерировать все необходимые классы. Это может или не может быть правильным для вашей ситуации.

больше информации: http://weblogs.asp.net/scottgu/archive/2007/05/19/using-linq-to-sql-part-1.aspx

...