Транзакция в рамках транзакции в C#

Я импортирую плоский файл счетов в базу данных с помощью C#. Я использую TransactionScope для отката всей операции, если с проблемой встречаются.

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

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

Однако я задавался вопросом, могло ли это быть сделано другой путь. Создание "вложенной" транзакции вокруг счета, вставка строки заголовка и позиций, затем обновление счета, когда общая строка достигнута. Эта "вложенная" транзакция откатывала бы, если бы определено, что счет должен быть пропущен, но полная транзакция продолжилась бы.

Действительно ли это возможно, практично, и как Вы настроили бы это?

8
задан Rosco 8 May 2010 в 16:21
поделиться

5 ответов

Это выполняется с помощью транзакции точка сохранения . Обычно это выглядит примерно так:

BEGIN TRANSACTION
for each invoice
   SAVE TRANSACTION InvoiceStarted
   BEGIN TRY
     Save header
     Save line 1
     Save line 2
     Save Total
   END TRY
   BEGIN CATCH
     ROLLBACK TO Invoicestarted 
     Log Failed Invoice
   END CATCH
end for
COMMIT

Я использовал псевдокод на основе Transact-SQL, и это не случайно. Точки сохранения - это концепция базы данных, и транзакции .Net их не поддерживают. Вы можете использовать SqlTransaction напрямую и использовать SqlTransaction.Save или вы можете использовать хранимые процедуры T-SQL, смоделированные на основе безопасного шаблона . Я бы рекомендовал вам избегать транзакций .Net (например, TransactionScope) в этом случае.

3
ответ дан 5 December 2019 в 04:41
поделиться

Ни TransactionScope , ни SQL Server не поддерживают вложенные транзакции.

Вы можете вкладывать экземпляры TransactionScope , но это только внешне выглядит как вложенная транзакция. На самом деле существует так называемая «внешняя» транзакция, и единовременно может быть только одна транзакция. Какая транзакция является внешней транзакцией, зависит от того, что вы используете для TransactionScopeOption при создании области.

Чтобы объяснить более подробно, рассмотрим следующее:

using (var outer = new TransactionScope())
{
    DoOuterWork();

    using (var inner1 = new TransactionScope(TransactionScopeOption.Suppress))
    {
        DoWork1();
        inner1.Complete();
    }

    using (var inner2 = new TransactionScope(TransactionScopeOption.RequiresNew))
    {
        DoWork2();
        inner2.Complete();
    }

    using (var inner3 = new TransactionScope(TransactionScopeOption.Required))
    {
        DoWork3();
        inner3.Complete();
    }

    outer.Complete();
}

Вот что происходит для каждой из внутренних областей:

  • inner1 выполняется в неявной транзакции, независимо от external . Ничего из того, что происходит в DoWork1 , не будет атомарным. Если это не удастся на полпути, у вас будут противоречивые данные. Любая работа, которая здесь происходит, всегда совершается, независимо от того, что происходит с внешним .

  • inner2 выполняется в новой транзакции, независимо от external .Эта транзакция отличается от внешней , но она не вложенная. В случае сбоя работа, выполненная в внешнем ( DoOuterWork () ) и любой другой области действия, все еще может быть зафиксирована, но вот загвоздка: Если она завершится, затем откат всей внешней транзакции не откатит назад работу, выполненную внутри inner2 . Вот почему он не является действительно вложенным. Кроме того, inner2 не будет иметь доступа ни к каким строкам, заблокированным external , поэтому вы можете столкнуться с тупиками здесь, если не будете осторожны.

  • inner3 выполняется в той же транзакции, что и external . Это поведение по умолчанию. Если DoWork3 () терпит неудачу и inner3 никогда не завершается, то выполняется откат всей внешней транзакции. Точно так же, если inner3 завершается успешно, но external откатывается, то любая работа, выполненная в DoWork3 () , также откатывается.

Итак, вы можете надеяться, что ни одна из этих опций на самом деле не вложена и не даст вам того, что вы хотите. Параметр Required приближается к вложенной транзакции, но не дает вам возможности независимо фиксировать или откатывать определенные единицы работы внутри транзакции.

Наиболее близким к истинным вложенным транзакциям в SQL Server является оператор SAVE TRAN в сочетании с некоторыми блоками TRY / CATCH .Если вы можете поместить свою логику в одну или несколько хранимых процедур, это будет хорошим вариантом.

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

37
ответ дан 5 December 2019 в 04:41
поделиться

Неудачная внутренняя транзакция приведет к откату внешней транзакции, поэтому вы не сможете пойти по этому пути.

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

0
ответ дан 5 December 2019 в 04:41
поделиться

Вместо использования вложенных транзакций вы можете создать транзакцию для каждого счета-фактуры. Таким образом, будут происходить только успешные обновления для всех счетов.

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

2
ответ дан 5 December 2019 в 04:41
поделиться

Лично я бы сначала посмотрел, нужно ли добавить счет-фактуру - если да, то сделайте вставку (в транзакции). В противном случае просто переходите к следующему счету-фактуре.

Я не думаю, что это так уж здорово - вставлять, а затем делать откат описанным вами способом.

2
ответ дан 5 December 2019 в 04:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: