2012-03-13 18 views
5

Estoy tratando de evitar la escalada de MSDTC en mi aplicación. Estoy usando LINQ con SQL Server Express 2008 R2, y luego usaré la versión completa.TransactionScope anidado y/o conexiones anidadas que provocan la escalada MSDTC

He escrito una clase de contenedor de base de datos que crea conexiones según sea necesario y las descarta lo más rápido posible. La cadena de conexión sigue siendo la misma en todas las conexiones.

Aquí es una versión muy reducida de mi clase:

public class SqlServerDatabaseWrapper { 

    public SqlServerDatabaseWrapper(string connectionString) { 
    ConnectionString = connectionString; 
    } 

    public string ConnectionString { get; private set; } 

    private static IDbConnection GetOpenConnection() { 
    var conn = new SqlConnection(ConnectionString); 
    conn.Open(); 
    return conn; 
    } 

    // there is also a second method to return a value 
    // there is PerformCommandAction for SqlCommand as well 
    public void PerformDataContextAction<TContext>(Func<IDbConnection, TContext> creator, Action<TContext> action) where TContext : DataContext { 
    PerformConnectionAction(conn => { 
     using (var context = creator(conn)) 
     action(context); 
    }); 
    } 

    // there is also a second method to return a value 
    public void PerformConnectionAction(Action<IDbConnection> action) { 
    using (IDbConnection conn = GetOpenConnection(ConnectionString)) { 
     action(conn); 
    } 
    } 
} 

Se utiliza de la siguiente manera:

var db = new SqlServerDatabaseWrapper(connectionString); 
db.PerformDataContextAction(
    conn => new SomeDataContext(conn), 
    context => { /* do something */ } 
); 

Si pongo un bloqueo en torno al contenido del método PerformConnectionAction, por lo que sólo una puede ejecutarse a la vez, luego todo funciona, pero hay una penalización de rendimiento notable. Sin embargo, cuando lo elimino, se intensifica.

El código que está utilizando el contenedor utiliza un TransactionScope, y podría haber anidamiento de TransactionScopes y/o llamadas a PerformDataContextAction o PerformConnectionAction (cada uno de los cuales crea una nueva conexión con la misma cadena de conexión); en pseudo-código (ya que esto podría ocurrir a través de diferentes clases/métodos):

var db = new SqlServerDatabaseWrapper(connectionString) 
using (TransactionScope tran = new TransactionScope()) { 
    db.PerformDataContextAction( 
    /* ... */, 
    context => { 
     using (TransactionScope tran2 = new TransactionScope()) { 
     db.PerformConnectionAction(conn => { /* some stuff */ }); 
     tran2.Complete(); 
     } 
    } 
    tran.Complete(); 
} 

Tenga en cuenta también que hay un uso de los métodos de miembros estáticos que podrían ocurrir en varios puntos.

También me gustaría añadir que la cadena de conexión es el siguiente:

Data Source=.\SQLEXPRESS;Initial Catalog=db1;User Id=test1;Password=test1;MultipleActiveResultSets=true;Enlist=false; 

La pregunta es, ¿cómo refactorizar/reescribir mi código para que mi aplicación puede funcionar bien, sin MSDTC, y sin bloqueos que introducen ?

Gracias

+0

¿Qué versión de SQL Server estás usando? – Oded

+0

SQL Express 2008 R2, que luego se incluirá en la versión completa – enashnash

+0

Estoy un poco desconcertado porque agregar un bloqueo evita la escalada, ya que 'TransactionScope's están enlazados a hilos, y los bloqueos solo afectan la interacción de los hilos, pero no el orden de operaciones dentro de un subproceso, a menos que su subprocesamiento introduzca un comportamiento no determinista, en el que pude ver cómo los bloqueos cambian las cosas. Si todavía está interesado en una respuesta, háganos saber más sobre lo que sucede en el hilo. –

Respuesta

1

¿Está utilizando una sola conexión a la base de datos dentro del ámbito de transacción? La creación de dos conexiones con una cadena de conexión igual o diferente dentro de un alcance de transacción escalará la transacción a una distribuida.

Puede almacenar la conexión en una variable estática de hilo y cerrarla/desecharla cuando finaliza todo el trabajo de la transacción. Entonces cada hilo tendrá su propia conexión.

Cuando agrega un bloqueo a su lógica, probablemente no obtenga una transacción distribuida porque el grupo de conexiones le devolverá la misma conexión cada vez.

Cuestiones relacionadas