2012-03-13 11 views
5

Sto cercando di evitare l'escalation MSDTC nella mia applicazione. Sto usando LINQ con SQL Server Express 2008 R2 e in seguito utilizzerò la versione completa.Nested TransactionScope e/o connessioni nidificate che provocano l'escalation MSDTC

Ho scritto una classe wrapper del database che crea le connessioni necessarie e le elimina il più rapidamente possibile. La stringa di connessione rimane la stessa su tutte le connessioni.

Ecco una versione molto più sintetica della mia classe:

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); 
    } 
    } 
} 

utilizzati come segue:

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

Se metto un blocco intorno al contenuto del metodo PerformConnectionAction, in modo che solo una può essere eseguito in un momento, quindi tutto funziona ma c'è una penalità di prestazioni notevole. Tuttavia, quando lo rimuovo, si intensifica.

Il codice che utilizza il wrapper utilizza TransactionScope e potrebbe essere nidificato TransactionScopes e/o chiamate a PerformDataContextAction o PerformConnectionAction (che creano ciascuna una nuova connessione con la stessa stringa di connessione); in pseudo-codice (poiché potrebbe verificarsi tra diversi classi/metodi):

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(); 
} 

noti anche che non v'è l'uso dei metodi Membership statici che potrebbero verificarsi in vari punti.

Vorrei anche aggiungere che la stringa di connessione è la seguente:

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

La domanda è: come faccio a refactoring/riscrivere il mio codice in modo che la mia applicazione in grado di eseguire bene, senza MSDTC, e senza serrature introducendo ?

Grazie

+0

Quale versione di SQL Server si sta utilizzando? – Oded

+0

SQL Express 2008 R2, in seguito essere immesso nella versione completa – enashnash

+0

Sono un po 'sconcertato che l'aggiunta di un blocco impedisce l'escalation, poiché 'TransactionScope' sono legati al thread e i blocchi influiscono solo sull'interazione dei thread, ma non sull'ordine di operazioni all'interno di un thread, a meno che il threading non introduca un comportamento non deterministico, in cui potrei vedere come le serrature cambiano le cose. Se sei ancora interessato a una risposta, ti preghiamo di farci sapere di più su ciò che accade sul thread-wise. –

risposta

1

Si sta utilizzando una sola connessione al database all'interno del campo di applicazione della transazione? La creazione di due connessioni con la stessa o una stringa di connessione diversa all'interno di un ambito di transazione comporta l'escalation della transazione a una versione distribuita.

È possibile memorizzare la connessione in una variabile statica del thread e chiuderla/eliminarla quando tutto il lavoro nella transazione è terminato. Quindi ogni thread avrà la propria connessione.

Quando si aggiunge un blocco alla logica, probabilmente non si otterrà una transazione distribuita poiché il pool di connessione restituirà sempre la stessa connessione.