293 votes

TransactionScope escalade automatiquement MSDTC sur certaines machines?

Dans notre projet, nous sommes à l'aide de TransactionScope de s'assurer que notre couche d'accès aux données effectue les actions d'une transaction. Nous visons à ne pas exiger le service MSDTC pour être activé sur les ordinateurs des utilisateurs.

La difficulté est, sur la moitié de nos développeurs de machines, nous pouvons exécuter avec MSDTC désactivé. L'autre moitié doit être activée ou qu'ils se la "MSDTC sur [SERVEUR] n'est pas disponible" message d'erreur.

Il m'a vraiment de me gratter la tête et m'a sérieusement envisagé de restauration d'un home-spun TransactionScope-comme solution basée sur ADO.NET transaction objets. C'est apparemment fous - le même code qui fonctionne (et ne dégénèrent pas) sur la moitié de nos développeurs ne dégénèrent sur les autres développeurs.

J'espérais une meilleure réponse à http://stackoverflow.com/questions/506733/ mais malheureusement, cela ne marche pas.

Voici un exemple de code qui sera la cause de la difficulté, sur les machines qui tentent de s'intensifier, il tente d'escalader sur la deuxième connexion.Open() (et oui, il n'y a pas d'autre connexion ouverte à l'époque.)

using (TransactionScope transactionScope = new TransactionScope() {
   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();
         using (SqlDataReader reader = command.ExecuteReader()) {
            // use the reader
            connection.Close();
         }
      }
   }

   // Do other stuff here that may or may not involve enlisting 
   // in the ambient transaction

   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();  // Throws "MSDTC on [SERVER] is unavailable" on some...

         // gets here on only half of the developer machines.
      }
      connection.Close();
   }

   transactionScope.Complete();
}

Nous avons vraiment cherché et essayé de comprendre cela. Voici quelques infos sur les machines qu'il fonctionne sur:

  • Dev 1: Windows 7 x64 SQL2008
  • Dev 2: Windows 7 x86 SQL2008
  • Dev 3: Windows 7 x64 SQL2005 SQL2008

Les développeurs qu'il ne fonctionne pas:

  • Dev 4: Windows 7 x64, SQL2008 SQL2005
  • Dev 5: Windows Vista x86, SQL2005
  • Dev 6: Windows XP X86, SQL2005
  • Mon PC à la Maison : Windows Vista Home Premium x86, SQL2005

Je dois ajouter que toutes les machines, dans un effort pour traquer le problème, ont été entièrement patché avec tout ce qui est disponible à partir de Microsoft Update.

Mise à jour 1:

Que MSDN opération d'augmentation progressive de la page indique les conditions suivantes sera la cause d'une opération de l'escalade à DTC:

  1. Au moins un durable de cette ressource qui ne prend pas en charge une seule phase de notifications est enrôlé dans la transaction.
  2. Au moins deux ressources durables qui prennent en charge une seule phase de notifications sont enrôlés dans la transaction. Par exemple, l'enrôlement d'une connexion unique à ne pas provoquer une promotion de la transaction. Cependant, chaque fois que vous ouvrez une deuxième connexion à une base de données causant la base de données pour s'enrôler, le Système.Les opérations d'infrastructure détecte que c'est la deuxième durable de cette ressource dans la transaction, et s'intensifie à un MSDTC transaction.
  3. Une demande de "maréchal" la transaction à un domaine d'application différent ou différents processus est invoqué. Par exemple, la sérialisation de l'objet de la transaction à l'échelle d'une limite du domaine d'application. L'objet de la transaction est dirigée par valeur, ce qui signifie que toute tentative de passer à travers une limite du domaine d'application (même dans le même processus) des résultats de la sérialisation de l'objet de transaction. Vous pouvez passer à la transaction, les objets en faisant un appel à une méthode à distance qui prend une Transaction en tant que paramètre ou vous pouvez essayer d'accéder à une distance transactionnelle-composant de service. Cette sérialise l'objet de la transaction et les résultats dans une escalade, comme lorsqu'une transaction est sérialisée sur un domaine d'application. Il est distribué et le local gestionnaire de transactions n'est plus adéquat.

Nous ne sommes pas expérimenter le #3. #2 n'est pas le cas, car il n'y a qu'une seule connexion à la fois, et c'est aussi à un seul "durable des ressources'. Est-il possible que #1 qui pourrait se passer? Certains SQL2005/8 configuration entraîne pas en charge une seule phase de notifications?

Mise à jour 2:

Re-étudié, personnellement, tous les versions de SQL Server - "Dev 3" a effectivement SQL2008, et "Dev 4" est en fait SQL2005. Ça m'apprendra à ne pas faire confiance à mes collègues de nouveau. ;) En raison de ce changement dans les données, je suis assez sûr que nous avons trouvé notre problème. Notre SQL2008 les développeurs n'étaient pas confrontés à ce problème parce SQL2008 a de grandes quantités de génial compris que SQL2005 n'a pas.

Il me dit aussi que parce que nous allons soutenir SQL2005 que nous ne pouvons pas utiliser TransactionScope comme nous l'avons été, et si nous voulons utiliser TransactionScope nous allons avoir besoin de passer un seul objet SqlConnection...autour de ce qui semble problématique dans les situations où l'occurrence de SqlConnection ne peut pas facilement être passé autour...ça sent mondiale-occurrence de SqlConnection. Pew!

Mise à jour 3

Juste pour clarifier jusqu'ici dans la question:

SQL2008:

  • Permet les connexions multiples au sein d'un seul TransactionScope (comme illustré dans l'exemple ci-dessus le code).
  • Avertissement n ° 1: Si ces multiples SqlConnections sont imbriqués, qui est, de deux ou de plus de SqlConnections sont ouvertes en même temps, TransactionScope immédiatement remonter à la DTC.
  • Avertissement n ° 2: Si une autre occurrence de SqlConnection est ouvert à un autre "durable des ressources' (c'est à dire: un autre Serveur SQL,), il sera immédiatement remonter à la DTC

SQL2005:

  • Ne pas autoriser les connexions multiples au sein d'un seul TransactionScope, période. Il va s'aggraver quand/si une deuxième occurrence de SqlConnection est ouvert.

Mise à jour 4

Dans l'intérêt de faire de cette question encore plus de désordre utile, et seulement pour les plus de souci de clarté, voici comment vous pouvez obtenir SQL2005 de l'escalade à DTC avec un seul SqlConnection:

using (TransactionScope transactionScope = new TransactionScope()) {
   using (SqlConnection connection = new SqlConnection(connectionString)) {
      connection.Open();
      connection.Close();
      connection.Open(); // escalates to DTC
   }
}

Cela semble juste cassé pour moi, mais je suppose que je peux comprendre si chaque appel à l' SqlConnection.Open() est saisissant de pool de connexion.

"Pourquoi cela peut-il arriver?" Eh bien, si vous utilisez un SqlTableAdapter à l'encontre de cette connexion avant qu'il est ouvert, le SqlTableAdapter permettra d'ouvrir et de fermer la connexion, effectivement la fin de la transaction pour vous, parce que maintenant vous ne pouvez pas l'ouvrir de nouveau.

Donc, en gros, pour utiliser avec succès TransactionScope avec SQL2005 vous avez besoin d'avoir une sorte de connexion global de l'objet qui reste ouvert à partir du point de la première TransactionScope est instancié jusqu'à ce qu'il n'est plus nécessaire. Outre le code-odeur d'un objet de connexion global, l'ouverture de la connexion de la première et de la fermeture de ce dernier est en désaccord à l'encontre de la logique de l'ouverture d'une connexion le plus tard possible et de le fermer dès que possible.

75voto

Joe Points 60749

SQL2008 pouvez utiliser plusieurs SQLConnections en TransactionScope sans augmenter, à condition que les connexions ne sont pas ouvertes en même temps, ce qui aurait pour résultat de plusieurs "physique" des connexions TCP et nécessitent donc d'escalade.

Je vois certains de vos développeurs ont SQL2005 et d'autres SQL2008. Êtes-vous sûr que vous avez correctement identifié ceux qui sont à l'escalade et qui n'est pas?

Le plus évident explication serait que les développeurs avec SQL 2008 sont ceux qui ne sont pas de l'escalade.

62voto

Peter Meinl Points 1002

Le résultat de mes recherches sur le sujet:

enter image description here

Voir Éviter l'Escalade pour les Transactions Distribuées

Je suis encore en train d'Oracle escalade de comportement: Faire des transactions couvrant de multiples connexions à la même DB remonter à la DTC?

33voto

hwiechers Points 4717

Ce code va provoquer une escalade lors de la connexion à 2005.

Vérifiez la documentation MSDN - http://msdn.microsoft.com/en-us/library/ms172070.aspx

Promus Transactions dans SQL Server 2008

Dans la version 2.0 de l' .NET Framework et SQL Server 2005, l'ouverture d'un deuxième la connexion à l'intérieur d'une TransactionScope serait automatiquement promouvoir l' transaction distribuée complète transaction, même si les deux connexions ont été en utilisant les mêmes connexion les chaînes de caractères. Dans ce cas, un distribué opération ajoute une surcharge inutile qui diminue les performances.

À partir de SQL Server 2008 et la version 3.5 de l' .NET Framework, les transactions locales ne sont plus promu transactions distribuées si une autre connexion est ouverte dans le transaction après la précédente la transaction est fermé. Cela nécessite aucune modification de votre code si vous déjà à l'aide de la connexion de la mutualisation et de la l'enrôlement dans les transactions.

Je ne peux pas expliquer pourquoi les Dev 3: Windows 7 x64, SQL2005 réussit et Dev 4: Windows 7 x64 échoue. Êtes-vous sûr que c'est pas l'inverse?

12voto

Chris Marisic Points 11495

Je ne sais pas pourquoi cette réponse a été supprimé, mais il semble que certaines informations pertinentes.

répondu le 4 Août '10 à 17:42 Eduardo http://stackoverflow.com/users/129960/eduardo

  1. Ensemble Enrôler=false sur la chaîne de connexion pour éviter automatique de l'enrôlement de la transaction.

  2. Manuellement inscrire connexion en tant que participants à l'étendue de transaction. (http://msdn.microsoft.com/en-us/library/ms172153%28v=VS.80%29.aspx)

ou cela: http://softwaredevelopmentsolutions.blogspot.com/2010/08/how-to-prevent-automatic-msdtc.html

2voto

Iftikhar Ali Points 38

Je ne suis pas sûr si la connexion imbriquée est le problème. J'appelle une instance locale du serveur SQL et il ne génère pas le DTC ??

     public void DoWork2()
    {
        using (TransactionScope ts2 = new TransactionScope())
        {
            using (SqlConnection conn1 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;"))
            {
                SqlCommand cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                cmd.Connection = conn1;
                cmd.Connection.Open();
                cmd.ExecuteNonQuery();

                using (SqlConnection conn2 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;Connection Timeout=100"))
                {
                    cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                    cmd.Connection = conn2;
                    cmd.Connection.Open();
                    cmd.ExecuteNonQuery();
                }
            }

            ts2.Complete();
        }
    }
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X