Можно ли использовать DependentTransaction или несколько DependentTransaction с TPL / Parallelism? - PullRequest
3 голосов
/ 12 августа 2011

Мне не удалось найти ничего похожего на то, что я делаю, и из-за проблем с бюджетом я вынужден обратиться к вам всем за помощью. В настоящее время я пишу приложение для импорта, которое может импортировать переменное количество записей. Скорее всего, это переменное число будет очень высоким, потенциально в миллионах. Мы используем потоковую передачу WCF для отправки фрагментов данных на сервер для обработки. На стороне сервера мы помещаем данные в временную таблицу SQL для проверки. Оттуда, используя TPL для извлечения заданного количества записей, а затем вставьте их в постоянное хранилище SQL-хранилища. Там гораздо больше, но в двух словах это основная часть. В настоящее время единственной нерешенной проблемой является то, что мы разрешаем пользователю отменять импорт в любое время. Это означает, что отмена может произойти во время сохранения этих записей. Нам нужно иметь возможность откатить все вставленные / обновленные записи. Поскольку мы используем TPL для обработки записей, мне крайне сложно делиться транзакциями. Я использую подход DependentTransaction, описанный в MSDN (очень похоже, http://msdn.microsoft.com/en-us/library/system.transactions.dependenttransaction(v=VS.100).aspx). Вот некоторые фрагменты кода:

var currentTransaction = System.Transactions.Transaction.Current;

...

// DataTable is simply a placeholder for X amount of records from the SQL temp table
var partitions = Partitioner.Create(0, DataTable.Rows.Count, 100);
Parallel.ForEach(partitions, (partition, state) =>
                    {
                        using (var parallelTransaction = new TransactionScope(currentTransaction.DependentClone((DependentCloneOption.RollbackIfNotComplete)))

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

Правильно ли я использую DependentTransaction? Может ли моя просьба быть возможной? Я знаю, что транзакции, как правило, являются последовательными и не используются в этом контексте, но для простоты и для того, чтобы избавить нас от необходимости реализовывать некоторые сумасшедшие функции для отслеживания вставленных записей и отката вручную, транзакция была бы идеальной!

Есть предложения?

ПРИМЕЧАНИЕ. Я пытался использовать подход локального состояния к параллельной итерации, но независимо от того, что я делаю, все это генерирует одно и то же исключение. Кроме того, использование RollbackIfNotComplete или BlockCommitUntilComplete не имеет никакого отношения.

1 Ответ

0 голосов
/ 03 мая 2013

Лучшее место для управления транзакциями SQL Server - это код T-SQL. Поскольку у вас уже есть все данные, которые вы хотите вставить / обновить / удалить в одном и том же SQL Server, я не вижу веской причины не просто выполнять выбор / вставку / обновление в транзакции в коде T-SQL. Вот простой шаблон, который вы можете использовать.

    -- REFERENCE: http://aleemkhan.wordpress.com/2006/07/21/t-sql-error-handling-pattern-for-nested-transactions-and-stored-procedures/
    -- ======================================================================================
    -- STANDARD HEADER FOR TRANSACTION LOGIC THAT WILL ALSO HANDLE BEING A NESTED TRANSACTION
    -- FOR SQL 2005 AND UP
    -- ======================================================================================
    SET XACT_ABORT ON;
    BEGIN TRY
    DECLARE @TranStarted bit; SET @TranStarted = 0
    IF( @@TRANCOUNT = 0 ) BEGIN  BEGIN TRANSACTION; SET @TranStarted = 1;  END ELSE SET @TranStarted = 0
    -- ======================================================================================




    -- ==================================================================
    -- ***** SQL CODE FOR SELECTS/INSERTS/UPDATES/DELETES GOES HERE *****
    -- ==================================================================




    -- ======================================================================================
    -- STANDARD FOOTER FOR TRANSACTION LOGIC THAT WILL ALSO HANDLE BEING A NESTED TRANSACTION 
    -- FOR SQL 2005 AND UP
    -- ======================================================================================
    IF( @TranStarted = 1 AND (XACT_STATE()) = 1 ) BEGIN  SET @TranStarted = 0; COMMIT TRANSACTION;  END
    RETURN(0)
    END TRY
    BEGIN CATCH
      DECLARE @ErrorMessage  nvarchar(4000)
             ,@ErrorNumber   int
             ,@ErrorSeverity int
             ,@ErrorState    int;
       SELECT @ErrorMessage  = ERROR_MESSAGE() + CHAR(13) + 'Actual Code Line that took the error: ' + CONVERT(nvarchar, ERROR_LINE())
                                               + CHAR(13) + 'Actual Proc that took the error: ' + CONVERT(nvarchar(126), ERROR_PROCEDURE())
             ,@ErrorSeverity = ERROR_SEVERITY()
             ,@ErrorState    = ERROR_STATE()
             ,@ErrorNumber   = ERROR_NUMBER();

    IF( @TranStarted = 1 AND (XACT_STATE()) = -1 ) BEGIN  SET @TranStarted = 0; ROLLBACK TRANSACTION; END
    RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
    END CATCH
    -- ======================================================================================
...