TransactionScope: Evitare Le Transazioni Distribuite

Ho un oggetto padre (parte di un DAL) che contiene, tra le altre cose, una raccolta (List<t>) di oggetti figlio.

Quando sto salvando l’oggetto nuovamente il DB, devo inserire/aggiornare il padre, e poi loop attraverso ogni bambino. Per la manutenzione, ho messo tutto il codice per il bambino e ‘ un metodo privato separato.

Stavo per usare ADO standard Transazioni, ma i miei viaggi, mi sono imbattuto in oggetto TransactionScope, che credo mi permetterà di avvolgere tutto il DB interazione nel metodo di padre (insieme con l’interazione del bambino, il metodo) in una transazione.

Finora tutto bene..?

Così la prossima domanda è: come creare e utilizzare connessioni all’interno di questo TransactionScope. Ho sentito che l’utilizzo di connessioni multiple, anche se sono allo stesso DB può forzare TransactionScope a pensare che si tratta di una transazione distribuita (con il coinvolgimento di alcuni costosi DTC lavoro).

È il caso? O è, come mi sembra di leggere altrove, un caso che, utilizzando la stessa stringa di connessione (che si presta ad un pool di connessioni) va bene?

Più praticamente parlando, faccio…

  1. Creare connessioni separate in genitori & bambino (anche se con la stessa stringa di connessione)
  2. Creare una connessione nel genitore un passarlo come parametro (sembra goffo per me)
  3. Fare qualcosa di diverso…?

AGGIORNAMENTO:

, Mentre sembra che mi sarebbe OK usando il mio solito .NET3.5+ e SQL Server 2008+, un’altra parte di questo progetto sarà l’utilizzo di Oracle (10g) così potevo praticare una tecnica che può essere utilizzato in modo coerente in tutti i progetti.

Mi limiterò a passare la connessione attraverso il figlio metodi.


Opzione 1 Esempio Di Codice:

using (TransactionScope ts = new TransactionScope())
            {
                using (SqlConnection conn = new SqlConnection(connString))
                {
                    using (SqlCommand cmd = new SqlCommand())
                    {
                        cmd.Connection = conn;
                        cmd.Connection.Open();
                        cmd.CommandType = CommandType.StoredProcedure;

                        try
                        {
                            //create & add parameters to command

                            //save parent object to DB
                            cmd.ExecuteNonQuery();

                            if ((int)cmd.Parameters["@Result"].Value != 0)
                            {
                                //not ok
                                //rollback transaction
                                ts.Dispose();
                                return false;
                            }
                            else //enquiry saved OK
                            {
                                if (update)
                                {
                                    enquiryID = (int)cmd.Parameters["@EnquiryID"].Value;
                                }

                                //Save Vehicles (child objects)
                                if (SaveVehiclesToEPE())
                                {
                                    ts.Complete();
                                    return true;
                                }
                                else
                                {
                                    ts.Dispose();
                                    return false;
                                }
                            }
                        }
                        catch (Exception ex)
                        {
                            //log error
                            ts.Dispose();
                            throw;
                        }
                    }
                }
            }
  • Vedere TransactionScope automaticamente l’escalation di MSDTC su alcune macchine?. Ci sono diverse buone risposte, ma quello che ho linkato è la più succinta (e relative alla tua domanda). Il risultato è che, se si utilizza .NET 2.0 e SQL Server 2005, potrai degenerare anche utilizzando due connessioni con la stessa stringa di connessione. Questo non è un problema .NET 3.5 e SQL Server 2008.
  • Sto usando .NET 3.5/4 e SQL 2008 in generale, ma di tanto in tanto potrei utilizzare SQL2005/2000, quindi vale la pena di ricordare comunque. Grazie
  • qualcuno mi può dare qualche conoscenza di ciò che è transazione distribuita. spiegare con un esempio.
InformationsquelleAutor CJM | 2010-07-06



3 Replies
  1. 24

    Molti database ADO provider (ad esempio Oracle ODP.NET), infatti, iniziano le transazioni distribuite quando si utilizza TransactionScope per le transazioni su più connessioni – anche quando condividono la stessa stringa di connessione.

    Alcuni provider, (come SQL2008 in .NET 3.5+) riconosce quando viene creata una nuova connessione in un ambito di transazione che si riferisce alla stessa stringa di connessione, e non comporta la DTC lavoro. Ma qualsiasi varianza nella stringa di connessione (come la messa a punto dei parametri) può precludere questo avvenimento – e il comportamento tornerà a utilizzare una transazione distribuita.

    Purtroppo, l’unico mezzo affidabile per garantire che il vostro transazioni di lavorare insieme, senza la creazione di una transazione distribuita è quello di passare l’oggetto di connessione (o il IDbTransaction) per i metodi che devono “continuare” la stessa transazione.

    A volte aiuta a elevare la connessione a un membro della classe in cui si sta facendo il lavoro, ma questo può creare situazioni imbarazzanti – e complica il controllo della vita e lo smaltimento dell’oggetto connection (poiché generalmente preclude l’uso dei using istruzione).

    • Ho avuto l’impressione che il Collegamento deve essere realizzato all’interno di TransactionScope per essere coperti. Sto indovinando che semplicemente passando la Connessione è più semplice e più ordinato di capire qualche altra soluzione?
    • Sapete se questo è cambiato con Oracle 12c? Vedo contrassegnato come “Disponibile in ODAC 12c o poi.” qui: apex.oracle.com/pls/apex/…
  2. 2

    Empiricamente, ho deciso (per il provider SQL Server) se il processo può sfruttare il pool di connessioni per condividere la connessione (e la transazione) tra i processi padre e figlio, il DTC non necessariamente essere coinvolti.

    Questo è un grande “se”, tuttavia, secondo il tuo esempio, la connessione creata dal processo padre non può essere condivisa dai processi figli (non si chiude/rilasciare la connessione prima di richiamare il figlio di processi). Questo si tradurrà in una transazione che si estende su due connessioni effettive, che comporterà la transazione per essere promosso a una transazione distribuita.

    Sembra che sarebbe stato facile per il refactoring del codice per evitare questo scenario: basta chiudere la connessione creata dal processo padre prima di richiamare il figlio processi.

    • Stai dicendo che se mi passa attraverso l’oggetto di connessione, ho bisogno di chiudere il padre e riaprire il bambino?
    • No. Stavo supponendo che si sta tentando di approfittare della convenienza di TransactionScope senza causare la transazione per essere promosso a una transazione distribuita. Sembra che questo non è possibile quando il targeting i database di Oracle, ma è possibile targeting database di SQL Server…così a lungo come le connessioni possono essere messi in comune e c’è solo una connessione aperta a un tempo.
  3. 1

    Nel tuo esempio TransactionScope è ancora nel contesto di un metodo, si potrebbe semplicemente creare un SqlTransaction con più comandi di sotto di quello. Utilizzare TransactionScope se si desidera spostare la transazione al di fuori di un metodo, per dire, il chiamante del metodo, o se si ha accesso a più database.

    Aggiornamento: mai mente ho appena avvistato il bambino di chiamata. In questa situazione, si potrebbe passare l’oggetto di connessione per le classi figlio. Inoltre, non è necessario manualmente smaltire TransactionScope – utilizzo di blocchi di agire come blocco try-finally ed eseguirà l’smaltire anche le eccezioni.

    Aggiorna 2: meglio ancora, passare il IDbTransaction per il bambino in classe. La connessione può essere recuperata da che.

    • Sì, avevo capito che la disposizione non era esplicitamente necessaria, ma quando ho retrofit TransactionScope al mio codice, chiaramente avevo dimenticato e ha continuato a convertire il mio vecchio trm.Istruzioni di Rollback per ts.Smaltire senza pensare. Buon posto!
    • Re Update 2 -Si può solo passare il IDbConnection per il bambino in classe. Se si utilizza IbConnection.BeginTransaction(), tutti i comandi creati con IDbConnection.CreateCommand() avranno automaticamente la transazione associati. Questo è il migliore perché è uno di meno parametro di trasmettere la vostra gerarchia, e riduce l’accoppiamento come un bambino a classi non è necessario essere preoccupati se sono in esecuzione all’interno di una transazione o non.

Lascia un commento