2010-04-02 10 views
9

Ho progettato la parte di accesso ai dati del nostro framework in modo che ogni volta che un oggetto business (BO) deve interagire con il database, dovrebbe aprire una connessione, richiamare l'accesso ai dati layer (per eseguire la query), quindi chiudere la connessione. Quindi, se fosse necessario eseguire una transazione, aprirà la connessione, inizierà la transazione, invocherà il livello di accesso ai dati (per eseguire la query) e quindi eseguirà il commit della transazione, chiuderà la transazione e infine chiuderà la connessione.Apertura e chiusura di una connessione al database all'interno di una transazione

Ho fatto in questo modo nella mentalità di "aperto in ritardo, chiudere presto" ... ma cosa succede se ho bisogno di chiamare altri BO per inviare i dati in una singola transazione? C'è un modo migliore per gestire le connessioni di apertura e chiusura e lavorare con le transazioni?

Sono un debuttante nel progettare l'architettura delle applicazioni, quindi spero di non farlo in modo errato ... ogni aiuto è apprezzato.

risposta

5

Se un determinato oggetto business ha bisogno di eseguire vari metodi in una transazione, utilizzare un TransactionScope in questo modo:

using (var transactionScope = new TransactionScope()) 
{ 
    this.Save(); 
    childObjA.Save(); 
    childObjB.Save(); 
    childObjC.Save(); 
    childObjD.Save(); 

    transactionScope.Complete(); 
} 

Se uno qualsiasi degli oggetti genera un'eccezione, verrà rollback della transazione.

Vedere the MSDN reference page for TransactionScope per ulteriori informazioni.

+3

Anche se 'TransactionScope' * è * la classe da utilizzare qui, con la progettazione corrente dell'OP provocherà una transazione distribuita o eventualmente un'eccezione se MSDTC è disabilitato o bloccato. È * probabilmente * non il risultato desiderato. – Aaronaught

+1

@Aaronaught, con SQL Server 2008 e .NET 3.5 la transazione non verrà promossa su una transazione distribuita (per la progettazione corrente). –

+2

@Tuzo: true, con SQL Server 2008 è possibile ingannare il problema di connessione multipla ** SE ** tutte le connessioni sono sullo stesso database ** e ** è sempre aperta solo una allo stesso tempo. È ancora una pratica piuttosto discutibile, specialmente quando il design alternativo è più facile da costruire/mantenere comunque. – Aaronaught

2

Sembra che tu abbia avuto l'idea giusta. Se è necessario coinvolgere più BO, uno di questi deve essere un "controller" — deve aprire e chiudere la connessione e passarlo agli altri. O qualche oggetto "wrapper" può gestire la connessione e passarla a ciascuno dei BO. È possibile che i BO debbano essere progettati per funzionare sia da soli (gestire la propria connessione), sia per accettare una connessione esistente dall'esterno.

4

Quando le astrazioni di livello superiore dipendono da astrazioni di livello inferiore (ad esempio classi di business logic in base alle connessioni dati), è comune fornire le astrazioni di livello inferiore tramite il costruttore. La tecnica si chiama costruttore iniezione:

public class OrderService 
{ 
    private SqlConnection connection; 

    public OrderService(SqlConnection connection) 
    { 
     if (connection == null) 
      throw new ArgumentNullException("connection"); 
     this.connection = connection; 
    } 

    // Other methods 
} 

Questo poi ti permette di scrivere codice contro i servizi simile al seguente:

using (TransactionScope tsc = new TransactionScope()) 
using (SqlConnection connection = new SqlConnection(...)) 
{ 
    connection.Open(); 
    OrderService os = new OrderService(connection); 
    os.ProcessOrder(myOrder); 
    ShippingService ss = new ShippingService(connection); 
    ss.ShipOrder(myOrder); 
    tsc.Complete(); 
} 

che è più probabile sta per essere ciò che si desidera, in Alla fine - la possibilità di condividere una connessione tra molti servizi.

Questo aiuta anche a disaccoppiare i servizi dai dettagli dell'implementazione della connessione dati. In questo modo, se vuoi fare qualcosa come cambiare le impostazioni di connessione in determinate circostanze, non devi scavare nei dettagli di 50 servizi diversi, devi solo cambiare la riga di codice che crea la connessione.

Un'ultima cosa: se si sta utilizzando lo TransactionScope, assicurarsi di aggiungere Transaction Binding=Explicit Unbind alla stringa di connessione, altrimenti è effettivamente possibile finire con dati incoerenti se una transazione è scaduta.

+1

In base a msdn, "A partire da .NET Framework versione 4, le modifiche a Implicit Unbind rendono obsoleto Unbind obsoleto." http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectionstring(v=vs.100).aspx Ottima risposta ed esempio! – xkingpin

2

Come menzionato da altri, TransactionScope è la strada da percorrere.

Se si utilizza SQL Server 2008 e .NET 3.5, modificherei la progettazione affinché l'oggetto business controlli la transazione e lasci l'apertura e la chiusura della connessione al livello dati.

Con il pool di connessioni attivato, non si incorre in alcun modo nel sovraccarico dell'apertura di una connessione fisica al database e le connessioni saranno aperte solo quando si esegue il lavoro effettivo. Poiché (presumo) hai SQL Server 2008 with .NET 3.5 your transaction will not escalate to a distributed transaction (a meno che non apri più connessioni contemporaneamente) in modo da ottenere il meglio da entrambi i mondi.

allora si potrebbe scrivere il vostro oggetto business come questo:

using (TransactionScope transactionScope = new TransactionScope()) 
{ 
    DataObject dataObject = new DataObject(); 
    dataObject.UpdateQuantity(...); 

    ShippingManager shippingManager = new ShippingManager(); 
    shippingManager.ShipOrder(...); 

    transactionScope.Complete() 
} 

Questo evita di dover passare le stringhe di connessione in giro a tutti gli oggetti di business e rende coordinare le transazioni facile.

Aggiornamento

La bellezza di System.Transactions è che tutte le transazioni sono gestite per voi, indipendentemente dalla connessione che si sta utilizzando. Basta dichiarare TransactionScope e tutti gli accessi al database all'interno di quel TransactionScope si verificheranno con una singola transazione (a meno che non si richieda diversamente con le diverse impostazioni di TransactionScope).

In passato (SQL Server 2005 .NET 2.0), se si apriva e si chiudeva una connessione e quindi si apriva e si chiudeva un'altra connessione (anche con la stessa stringa di connessione), la transazione veniva promossa da una transazione leggera a una distribuita Transazione. Ciò non era auspicabile in quanto le prestazioni risentono (la comunicazione con MSDTC è fuori processo e il protocollo di commit a due fasi) e MSDTC può essere difficile da configurare in molti ambienti di produzione (firewall e sicurezza).

Con SQL Server 2008 e .NET 3.5 hanno aggiunto la possibilità di evitare questa promozione quando si aprono e si chiudono più connessioni con la stessa stringa di connessione all'interno di una singola transazione. Per una spiegazione davvero buona di ciò che hanno visto Extending Lightweight Transactions in SqlClient.

Aggiornamento 2

Rapporti con Oracle 10g funzioneranno correttamente con TransactionScope. E sembra ODP.NET supports Lightweight Transactions (che è bello). Sfortunatamente, penso che la promozione di una transazione distribuita avverrà con la chiusura e l'apertura delle connessioni.

Se si desidera evitare una transazione distribuita, è possibile passare la connessione a ogni chiamata di metodo/oggetto business. Se non si desidera passare una connessione, è possibile utilizzare uno ConnectionScope class che mantiene aperta la connessione sul thread. Un'alternativa sarebbe quella di utilizzare il blocco di applicazioni di accesso ai dati di Enterprise Library 3.0 (e versioni successive). Data Access Block can detect that a transaction is in progress and use the same connection per evitare una transazione distribuita.

+0

Se il livello dati gestisce le connessioni di apertura e chiusura, come si usano le transazioni? Se hai tre BO che devono fare qualcosa e sono tutti sulla stessa transazione, non dovrebbero tutti usare la stessa connessione? –

+0

Stavo pensando di utilizzare il blocco Data Access in Enterprise Library. Non sono sicuro di avere abbastanza tempo per riscrivere quella parte della mia domanda. Dovrò usare EL o passare la connessione a ciascuno dei miei BO. Le mie BO e le Entità sono le stesse, quindi potrei aver bisogno di romperle per farlo funzionare meglio. –

1

Probabilmente stai cercando il modello Unit of Work e il modello Registry. Questi due modelli possono lavorare di concerto per separare le preoccupazioni di trovare oggetti di business e rintracciarli per il successivo commit al tuo data store come transazione.

Vorrei anche esaminare Object Relational Mapping, o ORM. ORM è una composizione di livello superiore dell'unità di lavoro, registro, ignoranza della persistenza e altri modelli che fornisce una separazione molto netta della logica aziendale dalla logica di persistenza. Utilizzando e ORM, puoi generalmente eliminare la necessità di scrivere stored procedure, creare un DAL personalizzato, ecc. L'ORM si prende cura dei tuoi problemi di persistenza per te, permettendoti di concentrarti sulla logica di business che deve essere fatta.

Poiché si utilizza C# e .NET, vorrei esaminare Entity Framework (v4, non utilizzare v1) o LINQ to SQL. Entrambi sono mapper OR forniti con .NET Framework dalla v3.5 in poi. LINQ to SQL è un ORM molto semplice e ben strutturato che dovrebbe farti andare molto velocemente. Entity Framework è un ORM molto più ricco che è anche ben equipaggiato (meglio di LINQ a SQL) e offre molte più funzionalità. Ci sono anche ORM di terze parti che possono fare il lavoro, incluso uno gratuito chiamato NHibernate. Anche se non è ben equipaggiato come gli ORM di Microsoft, NHibernate è un ORM open source molto maturo con una vasta comunità che segue.

Se un ORM non è una possibilità, quindi vorrei guardare in Unit of Work, Registry (o Repository), Persistenza ignoranza, Separation of Concerns, Single Responsibility, e altri modelli correlati.

Problemi correlati