Parallel.Invoke (), TransactionScope () et SqlBulkCopy

c# multithreading sqlbulkcopy task-parallel-library transactionscope

Question

J'ai plusieurs méthodes à l'intérieur d'un Parallel.Invoke() qui doivent être exécutées à l'intérieur d'une transaction. Ces méthodes invoquent toutes des instances de SqlBulkCopy Le cas d'utilisation est "tout-ou-rien", ainsi, si une méthode échoue, rien n'est commis. Je reçois une TransactionAbortedException ({"Transaction Timeout"}) lorsque j'appelle la méthode Complete () sur la transaction parente.

Ceci est la transaction parente:

using (var ts = new TransactionScope())
     {
       var saveClone = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
       var saveErrorsClone = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
        var saveADClone = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
        var saveEnrollmentsClone = Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);
        Parallel.Invoke(_options, () =>
                    {
                        Save(data, saveClone);
                    },
                    () =>
                    {
                        SaveErrors(saveErrorsClone);
                    },
                    () =>
                    {
                        SaveEnrollments(data, saveEnrollmentsClone);
                    });
        ts.Complete();  
      }//***** GET THE EXCEPTION HERE *****

Voici une transaction dépendante qui utilise SqlBulkCopy (ils ont tous la même structure). Je passe le parent et l'assigne au TransactionScope l'enfant

private void Save(IDictionary<string, string> data, Transaction transaction)
        {
        var dTs = (DependentTransaction)transaction;

        if (transaction.TransactionInformation.Status != TransactionStatus.Aborted)
            {
               using (var ts = new TransactionScope(dTs))
                   {
                     _walmartData.Save(data);
                     Debug.WriteLine("Completed Processing XML - {0}", _stopWatch.Elapsed);
                      ts.Complete();
                    }
             }
        else
             {
                Debug.WriteLine("Save Not Executed - Transaction Aborted - {0}", _stopWatch.Elapsed);    
                dTs.Complete();
             }
        dTs.Complete();
}

EDIT (ajouté ma méthode SqlBulkCopy ... notice null pour le paramètre de transaction )

private void SqlBulkCopy(DataTable dt, SqlBulkCopyColumnMappingCollection mappings)
        {
            try
            {
                using (var sbc = new SqlBulkCopy(_conn, SqlBulkCopyOptions.TableLock, null))
                {
                    sbc.BatchSize = 100;
                    sbc.BulkCopyTimeout = 0;
                    sbc.DestinationTableName = dt.TableName;

                    foreach (SqlBulkCopyColumnMapping mapping in mappings)
                    {
                        sbc.ColumnMappings.Add(mapping);
                    }

                    sbc.WriteToServer(dt);
                }
            }
            catch (Exception)
            {
                throw;
            }
        }

En plus de corriger l'erreur, je suis ouvert aux alternatives. Merci.

Réponse acceptée

Après beaucoup de souffrances, de recherches et d'absence de réponse valable, je dois croire que ce n'est pas possible avec la pile que j'ai décrite dans ma question. Je pense que le problème est entre TransactionScope et SqlBulkCopy. Je mets cette réponse ici pour le bénéfice des futurs téléspectateurs. Si quelqu'un peut prouver que cela peut être fait, je supprimerai volontiers cette réponse.


Réponse populaire

Vous créez une forme de blocage avec votre choix de DependentCloneOption.BlockCommitUntilComplete .

Parallel.Invoke bloque le thread appelant jusqu'à ce que tout son traitement soit terminé. Les travaux essayant d'être terminés par Parallel.Invoke sont tous bloquants en attendant la fin de la transaction parent (en raison de DependentCloneOption). Alors les 2 se attendent l'un dans l'autre ... impasse. La transaction parente arrive finalement à expiration et libère le blocage des transactions dépendantes, ce qui débloque votre thread appelant.

Pouvez-vous utiliser DependentCloneOption.RollbackIfNotComplete ?



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi