2010-03-23 37 views
12

Tengo una rutina que usa un ciclo recursivo para insertar elementos en una base de datos de SQL Server 2005 La primera llamada que inicia el ciclo se incluye dentro de una transacción usando TransactionScope. Cuando llamo por primera vez a ProcessItem, los datos myItem se insertan en la base de datos como se esperaba. Sin embargo, cuando se invoca ProcessItem desde ProcessItemLinks o ProcessItemComments, aparece el siguiente error.¿Por qué el funcionamiento de TransactionScope no es válido?

“La operación no es válida para el estado de la transacción”

estoy corriendo en esta depuración con VS 2008 en Windows 7 y tienen el MSDTC se ejecuta para permitir las transacciones distribuidas. El código a continuación no es mi código de producción, pero se establece exactamente igual. AddItemToDatabase es un método en una clase que no puedo modificar y utiliza un ExecuteNonQuery estándar() que crea una conexión y luego se cierra y desecha una vez que se completa.

He visto otras publicaciones aquí e Internet y todavía no puedo resolver este problema. Cualquier ayuda sería muy apreciada.

using (TransactionScope processItem = new TransactionScope()) 
{ 
    foreach (Item myItem in itemsList) 
    { 
     ProcessItem(myItem); 
    } 
    processItem.Complete(); 
}  
private void ProcessItem(Item myItem) 
{ 
    AddItemToDatabase(myItem); 
    ProcessItemLinks(myItem); 
    ProcessItemComments(myItem); 
}  
private void ProcessItemLinks(Item myItem) 
{ 
    foreach (Item link in myItem.Links) 
    { 
     ProcessItem(link); 
    } 
} 
private void ProcessItemComments(Item myItem) 
{ 
    foreach (Item comment in myItem.Comments) 
    { 
     ProcessItem(comment); 
    } 
} 

Aquí está la parte superior del seguimiento de la pila. Lamentablemente, no puedo mostrar la compilación hasta este momento como información confidencial de la empresa que no puedo revelar.

at System.Transactions.TransactionState.EnlistPromotableSinglePhase(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Transaction atomicTransaction) 
    at System.Transactions.Transaction.EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification) 
    at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx) 
    at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx) 
    at System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction) 
    at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction) 
    at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject) 
    at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) 
    at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) 
    at System.Data.SqlClient.SqlConnection.Open() 
+0

Podría publicar el seguimiento de la pila de la excepcion? –

+0

He agregado la mayor cantidad de trazas permitidas. Espero que sea suficiente! – Cragly

+0

¿Puedes publicar la excepción completa? Publique la salida de ex.ToString(), luego elimine cualquier cosa sensible. Me pregunto si hay una InnerException o dos. –

Respuesta

23

Las transacciones distribuidas hizo que mi pelo gris prematuramente ir :)

sospechosos habituales

  1. Firewall está bloqueando MSDTC
  2. su transacción es el tiempo de espera por alguna razón (intente aumentar el tiempo de espera)
  3. Tiene otro alcance de transacción en algún lugar en la parte superior del código que está jugando con la transacción actual

Probar si MSDTC está funcionando bien usando herramientas como DTCPing

También prueba mediante la inserción de un pequeño número de elementos en un principio. Su código parece estar en un bucle recursivo que puede procesar una gran cantidad de datos. Tal vez está corriendo a muchas consultas y la transacción se está agotando.

Algún tiempo System.Transactions.Transaction.Current tiene algunas pistas sobre lo que sucedió. Agregar un reloj contra esta variable global

+0

¡Sí, mi cabello comenzó a caerse! Proporcionará una actualización sobre cómo me llevo con las sugerencias anteriores. – Cragly

+2

+1 por "mi cabello se volvió gris" ... es una historia real –

+1

Timeout me ha pillado un par de veces. Es fácil olvidarse de la transacción distribuida, especialmente cuando se depura. Vale la pena señalar que el valor predeterminado es de solo 1 minuto. Puede aumentarlo (por ejemplo, hasta 10 minutos) agregando esto a su app.config - '', o pasando como parámetro a 'TransactionScope()' – Rhumborl

0

El tiempo máximo predeterminado es de 10 minutos. Puede anular este se encuentra en el machine.config sin embargo:

<configuration> 
    <system.transactions> 
     <machineSettings maxTimeout="00:00:30" /> 
    </system.transactions> 
</configuration> 

O bien, puede utilizar la reflexión para anularlo en código:

private static void OverrideTransactionScopeMaximumTimeout(TimeSpan timeOut) 
    { 

     // 1. create a object of the type specified by the fully qualified name 

     Type oSystemType = typeof(global::System.Transactions.TransactionManager); 

     System.Reflection.FieldInfo oCachedMaxTimeout = oSystemType.GetField("_cachedMaxTimeout", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 

     System.Reflection.FieldInfo oMaximumTimeout = oSystemType.GetField("_maximumTimeout", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); 

     oCachedMaxTimeout.SetValue(null, true); 

     oMaximumTimeout.SetValue(null, timeOut); 

     // For testing to confirm value was changed 

     // MessageBox.Show(string.Format(&quot;DEBUG SUCCESS!! &nbsp;Maximum Timeout for transactions is &#39;{0}&#39;&quot;, TransactionManager.MaximumTimeout.ToString())); 

    } 

Más información: https://blogs.msdn.microsoft.com/ajit/2008/06/18/override-the-system-transactions-default-timeout-of-10-minutes-in-the-code/

Cuestiones relacionadas