2008-10-14 9 views
5

Cuando usamos una transacción de System.Transactions (creando TransationScope para una instancia) de forma predeterminada todas las conexiones Sql (System.Data.SqlClient.SqlConnection) (pero no es la verdadera para Oracle.DataAccess.OracleConnection) se alistan en la apertura. Eso se llama enrolamiento automático. Buena característica. Pero se puede desactivar a través del parámetro de una cadena de conexión (enlist = false). En ese caso, la conexión que se abre no se alistó. Pero puede enlistarse manualmente más tarde. Así que mi pregunta es: para alguna instancia de SqlConnection dada, ¿cómo puedo determinar si esa conexión se enlistó o no (en un System.Transaction). Puedo mirar la cadena de conexión para el parámetro. Pero esto no funcionará porque, como dije, la conexión podría enlistarse manualmente.¿Cómo se determina si SqlConnection se alistó en un System.Transactions 'tx o no?

+0

Buena pregunta. Mi escenario es que tengo un ensamblado que es solo un grupo de métodos estáticos que tienen un SqlConnection abierto y CRUD en él. Sería bueno hacer cumplir que SqlConnection se alistó en una transacción antes de continuar, para ayudar a proteger contra la corrupción lógica a nivel de aplicación por la persona que llama. Podemos detectar un 'Transaction.Current! = Null' y podemos detectar' SqlConnection conn! = Null', pero no podemos probar para ver si el conn se enlista en una transacción. –

Respuesta

7

El marco no parece permitir eso.

Tal vez podríamos discutir por qué necesita saber esta información? Las TransactionScopeOptions le dan cierta flexibilidad sobre cuándo crear transacciones.

Sin embargo, rechazando "no" como respuesta, una pequeña fuente navegando más tarde y he creado este código, que SI FUNCIONA. Tenga en cuenta que este código podría dejar de funcionar en cualquier momento con parches en el marco.

static bool IsEnlisted(SqlConnection sqlConnection) 
    { 
     object innerConnection = typeof(SqlConnection).GetField("_innerConnection", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy).GetValue(sqlConnection); 
     var enlistedTransactionField = 
      EnumerateInheritanceChain(innerConnection.GetType()) 
      .Select(t => t.GetField("_enlistedTransaction", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)) 
      .Where(fi => fi != null) 
      .First(); 
     object enlistedTransaction = enlistedTransactionField.GetValue(innerConnection); 
     return enlistedTransaction != null; 
    } 

    static IEnumerable<Type> EnumerateInheritanceChain(Type root) 
    { 
     for (Type current = root; current != null; current = current.BaseType) 
      yield return current; 
    } 

De nuevo, esto está haciendo uso de variables privadas y clases internas dentro del marco .NET. Si bien funciona hoy, puede que no mañana.

+1

+1 como advertencia "puede que no funcione mañana". – TcKs

+0

¿Hoy está funcionando? –

+0

Hoy está funcionando. – TheSoftwareJedi

Cuestiones relacionadas