2010-12-14 56 views
1

Mon application (service WCF) utilise des contextes de données LINQ et nous avons récemment décidé de tout emballer dans une transaction. Semble fonctionner plutôt bien; Chaque appel dans le service a sa propre transaction, donc si une exception est levée, tout est annulé et aucune modification n'est validée dans la base de données.Exception lors de la tentative de création/utilisation d'une transaction interne

Cependant, nous avons la notion d'un «code de superviseur», qui peut être généré et utilisé une seule fois. Nous souhaitons que le code de superviseur soit marqué comme étant utilisé même si une erreur survient plus tard dans l'opération. Pour moi, cela suggère que je devrais utiliser une transaction interne?

donc dans ma méthode SetToUsed, j'ai mis dans une nouvelle transaction comme ceci:

public void SetToUsed(string code) 
    { 
     // security codes explicitly need their own transaction so that they will still be marked as having been used even if the outer transaction were to be rolled back due to error 
     using (var securityCodeTransaction = new TransactionScope(
       TransactionScopeOption.RequiresNew, 
       new TransactionOptions 
       { 
        IsolationLevel = IsolationLevel.ReadUncommitted 
       }, 
       EnterpriseServicesInteropOption.Automatic)) 
     { 
      var returnedItems = m_safetyCodeRepository.FindAll(sc => sc.safetyCode == code && 
                    sc.safetyCodeTypeId == (long)GetCodeType() && 
                    sc.isUsed == false); 
      foreach (var item in returnedItems) 
      { 
       item.isUsed = true; 
       m_safetyCodeRepository.SaveChanges(item); 
      } 

      securityCodeTransaction.Complete(); 
     } 
    } 

Cependant, cela provoque une exception: System.InvalidOperationException: Connection currently has transaction enlisted. Finish current transaction and retry.
L'exception est levée sur la ligne FindAll, qui est une enveloppe mince pour

dataContext.GetTable<tbSftSafetyCodes>().Where(exp).ToList() 

Suis-je manque quelque chose, ou d'aller tout à fait à ce sujet dans le mauvais sens? Edit: Je me suis rendu compte que je n'avais pas réellement besoin d'une Transaction pour les modifications du Code Superviseur en tant que tel.
J'ai donc changé le TransactionScopeOption à TransactionScopeOption.Suppress à la place. Je voudrais toujours savoir pourquoi avoir la transaction interne en utilisant TransactionScopeOption.RequiresNew n'a pas fonctionné si!

+0

Comment la transaction externe (le cas échéant) est-elle créée? –

+0

@Mark: la TransactionScope externe est créée de la même manière, plus haut dans la pile. – Coxy

Répondre

4
TransactionScopeOption.RequiresNew  

tente de démarrer une transaction entièrement nouvelle, indépendante de celle dans laquelle elle se trouve, est-ce ce que vous avez l'intention de faire? L'option Nécessite inscrira la nouvelle transaction dans la transaction actuellement active.

Cependant, si vous voulez toujours que le code soit marqué comme utilisé - avez-vous besoin de l'avoir dans une transaction? Ne pourriez-vous pas simplement le marquer comme utilisé et valider cette modification juste avant de commencer votre opération encapsulée dans une transaction?

+0

Ouais, l'intention était que la transaction interne soit complètement indépendante de la transaction externe, donc elle serait toujours validée même si la transaction externe échouait. Nous imbriquons les transactions parce que nous ajoutons de la transactionnalité à une application existante et qu'elle correspond à l'architecture actuelle. – Coxy

0

Dans mon cas, à côté d'utiliser l'option TransactionScopeOption.RequiresNew il a été également nécessaire de précéder la déclaration en utilisant avec

dc.Connection.Open(); 
    dc.Connection.EnlistTransaction(Transaction.Current); 
    if (dc.Connection.State!=ConnectionState.Closed) dc.Connection.Close(); 

    using (var securityCodeTransaction = new TransactionScope(
      TransactionScopeOption.RequiresNew, 
      new TransactionOptions 
      { 
       IsolationLevel = IsolationLevel.ReadUncommitted 
      }, 
      EnterpriseServicesInteropOption.Automatic)) 
    { 
      // same code as in question 
    } 

qui a fait la question disparaître - mais, je ne sais pas pourquoi cela était nécessaire.

Remarque: Afin d'inscrire la transaction, la connexion doit être ouverte. Par conséquent, les instructions open + close sont requises.