是否可以使用DependentTransaction或多个DependentTransactions与TPL / Parallelism?

时间:2011-08-12 15:06:37

标签: tsql sql-server-2008 c#-4.0 transactions parallel-processing

我无法找到任何类似于我正在做的事情,由于预算问题,我不得不求助于你们所有人。我目前正在编写一个导入应用程序,可以导入可变数量的记录。更可能的是,这个可变数字将非常高,可能有数百万。我们利用WCF流将数据块发送到服务器进行处理。在服务器端,我们将数据转储到临时SQL表中以进行验证。从那里,使用TPL提取一定数量的记录,然后将它们插入永久SQL存储库。还有更多涉及,但简而言之,这是其中的大部分。目前唯一的突出问题是我们允许用户随时取消导入。这意味着在保存这些记录期间可能会发生取消。我们需要能够回滚所有插入/更新的记录。由于我们使用TPL处理记录,因此我很难分享交易。我正在使用MSDN上概述的DependentTransaction方法(非常相似,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 :(得分:0)

管理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
    -- ======================================================================================