2011-10-27 13 views
9

Mi chiedevo se la mia implementazione di seguito è il modo più efficiente per smaltire SQLconnection in questo caso.È questo il modo giusto per smaltire SQLConnection

Conosco normalmente se sto utilizzando SqlConnection direttamente Posso semplicemente racchiudere la connessione all'interno di un blocco utilizzando per eliminarlo automaticamente, ma in questo caso volevo mantenere la connessione aperta e disponibile per Tutti i metodi in SQLRespository classe.

public class SqlRepository : IRepository 
{ 
    private readonly string connectionString; 
    private SqlConnection connection; 

    public SqlRepository(string connectionString) 
    { 
     this.connectionString = connectionString; 
     connection = new SqlConnection(connectionString); 
     connection.Open(); 
    } 

    public void Method_A() 
    { 
     // uses the SqlConnection to fetch data 
    }  

    public void Method_B() 
    { 
     // uses the SqlConnection to fetch data 
    }  

    public void Dispose() 
    {    
     connection.Dispose(); 
    } 
} 

Usage:

using (IRepository repository = new SqlRepository(connectionString)) 
{ 
    var item = repository.items;  
} 

Aggiornamento IRepository fa implementare IDisposable

+0

Anche se è quasi sì, ma in caso di un'eccezione dopo l'apertura di una connessione nell'utilizzo del blocco, se si eseguono più operazioni, alcune eccezioni di lancio non disporranno mai dell'oggetto di connessione. E non è una buona idea rendere gli oggetti di connessione un ambito globale, ma giocare con CommandBehavior Vedi qui: http://stackoverflow.com/questions/7903326/why-does-my-successfully-read-npgsql-data-disappear/7903616 # 7903616 – Zenwalker

+0

Un altro suggerimento è, per favore, attenersi sempre alle linee guida e implementare il modello di smaltimento. E connection.Dispose() chiuderà la connessione per te, puoi controllare il suo IL e vedrai chiamare DBConnection.Close(). Quindi l'apertura di conn in Using() {} è sufficiente purché non vengano lanciate eccezioni. – Zenwalker

+0

@zenwalker '... a condizione che non vengano emesse eccezioni. La mia comprensione dell'istruzione' using' è che viene tradotta dal compilatore in un 'try'/finalmente', quindi è sempre disponibile anche nel caso di un eccezione. –

risposta

9

Non mantenere la connessione aperta per le chiamate. Stai sconfiggendo il pool di connessioni.

Se si sta lavorando con una connessione in pool (come sqlserver), verrà raccolta e riutilizzata. Basta aprire e chiudere all'interno del metodo a & b.

Si potrebbe obiettare che se il chiamante fa quello che hai fatto usando con una chiamata di metodo va bene. Ma se si utilizza {} con sqlconnection all'interno di ciascun metodo worker (1) il codice sarà più semplice e (2) si è certi che il pool non verrà sconfitto (ovvero gli elementi di tenuta fuori dal pool quando altre richieste potrebbero usarlo) .

EDIT:

Aggiunta pseudo sulla base dei commenti.

Il motivo è problematico perché un chiamante può fare.

//pseudo code 
using (SqlRepository r) 
{ 
    r.MethodA(); 

    // other code here that takes some time. your holding a connection 
    // out of the pool and being selfish. other threads could have 
    // used your connection before you get a chance to use it again. 

    r.MethodB(); 
} // freed for others here. 

Questo ucciderà la scalabilità del server - fidati. Ho visto sistemi molto grandi soffocati da questo - di solito perché si stanno estendendo transazioni lato AT.

Un modello migliore:

class Repository 
{ 
    void MethodA() 
    { 
     using (Sqlconnection) 
     { 
      // db call 
     } 
    } 

    void MethodB() 
    { 
     using (Sqlconnection) 
     { 
      // you can even have multiple calls here (roundtrips) 
      // and start transactions. although that can be problematic 
      // for other reasons. 
     } 
    } 

Ora, la piscina è più efficace. Mi rendo conto che la domanda era sul modello monouso - e sì, puoi farlo. Ma ...

Non permetterei che la connessione si estenda per tutta la durata del repository.

+1

"forse" - se vuoi il supporto per le transazioni, potresti avere un motivo per tenerlo aperto. le connessioni chiuse non supporteranno una connessione. I repository a volte iniettano/costruiscono un'altra classe (contesto, ecc.) Che può essere utilizzata su più repository (con o senza unità di lavoro) –

+0

Ok: opterebbe per le transazioni in SQL. Tenere la connessione fuori dal blocco pool su un dtc tx potrebbe essere un motivo, ma questo ha grossi inconvenienti a meno che non sia assolutamente necessario. Punto preso ma sarei costretto a farlo :) – bryanmac

+0

Le transazioni sono una preoccupazione trasversale e imho non appartengono al lato sql in generale se non strettamente necessario. Il codice lo supporta per un motivo e il codice dovrebbe sapere cosa deve essere salvato in una volta. Se questo va avanti sul lato sql server, allora stai decidendo di includere la logica sul lato db che inizia a complicarsi durante il test dell'unità. i miei pensieri comunque:) –

0

Se si vuole fare in quel modo, assicurarsi di implementare IDisposable (Non posso dirvi se si nota questo sulla tua interfaccia IRepository o meno) e farò qualcosa del tipo:

 

     private bool disposed = false; 

     protected virtual void Dispose(bool disposing) 
     { 
      if (!this.disposed) 
      { 
       if (disposing) 
       { 
        _context.Dispose(); 
       } 
      } 
      this.disposed = true; 
     } 

     public void Dispose() 
     { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
     } 

Ovviamente il _context qui è il tuo SqlConnection.

tuttavia - sei sicuro di volere una connessione per repository? Che dire delle operazioni che coprono più di un repository?

+0

Questo è un modello monouso migliore, ma la domanda è se la connessione debba essere mantenuta aperta attraverso chiamate di metodo quando altre richieste potrebbero riutilizzare quella connessione – bryanmac

+0

Penso che tu stia accennando a quella domanda 1 connessione per repository ... Molto male se servizio concorrente – bryanmac

+0

assolutamente dipende da quello che stai facendo. Se non stai utilizzando un'unità di lavoro basata su domini (rintracciare modelli/oggetti e poi inviarli), ti verrà lasciata la condivisione di una connessione per questo scopo se hai bisogno di supporto transazionale senza dover utilizzare il servizio DTC che richiede che questo sia configurato e in esecuzione su un sistema - alcuni scelgono di non farlo. Alcuni scelgono di gestirlo automaticamente, alcuni scelgono unità di lavoro, alcuni scelgono sessioni (nibernetico) o contesti db/oggetto (EF) per gestirlo. Il fatto è che sebbene le connessioni siano ancora abbastanza spesso passate in giro se il design lo richiede. –

0

Assumendo IRepository eredita dal IDisposable, l'implementazione va bene a patto di non stanno mantenendo un'istanza della classe SqlRepository aperto più a lungo di quello necessario per eseguire una sequenza di 'logica' di query sul database.Il fatto che questa sequenza possa comprendere diverse chiamate di metodo non è un problema.

Non sono d'accordo con la risposta di @ bryanmac:

non mantengono la chiamate spanning aprire la connessione. Stai sconfiggendo il pool di connessioni.

fornito la sequenza di chiamate di metodo appartengono insieme e non mantenere la connessione aperta più a lungo di quanto serve per completare la sequenza logica, non vedo come questo in alcun modo sconfigge il pool di connessioni.

Un commento però. Si dovrebbe:

  • O attuare la IDisposable modello standard (con un metodo protected void Dispose(bool disposing) che può essere ignorato nelle classi derivate

  • o fare la classe sigillata, nel qual caso il vostro IDisposable implementazione esistente va bene

    .
+0

Implementa IDisposable. Sono un po 'd'accordo con l'argomento del pool di connessioni, quando avvolgo l'uso del repository all'interno di un blocco di codice sto insinuando che il prossimo gruppo di metodi faccia parte di una transazione logica. Inoltre mi piace come guardando il codice è ovvio che il repository andrà al di fuori del blocco using e quindi tutte le connessioni verranno liberate. – newbie

+0

Dicendo che eviterai il problema del raggruppamento avvolgendo le chiamate consecutive in un blocco usando - stai dicendo che ridurrai al minimo l'impatto rendendo sempre l'ambito piccolo e le chiamate indietro. Il codice cambierà nel tempo: non lo garantirà il tuo codice. Lascia che il pool di connessioni assicuri che le connessioni vengano utilizzate in modo efficiente ... – bryanmac

+0

Stai dicendo che non sarà un problema "fornire ..." e "finché ...". il problema è che puoi dire che non sarà problematico fintanto che i chiamanti del tuo repository fanno certe cose. Ma perché? a cosa serve la connessione che viene portata alla durata del tuo repository? Ti permette di implementare un modello monouso fresco e questo è tutto. Se si esegue l'ambito di SqlConnection su ciascun metodo, il pool garantirà il * più * efficiente in quanto l'utilizzo di ciascuna connessione è granulare. Se fai una sequenza di chiamate, la piscina ti assicurerà che tu stia bene. – bryanmac

Problemi correlati