Best practice per la condivisione di IDbConnection o stringa / factory di connessione nel proprio codice .Net

StackOverflow https://stackoverflow.com/questions/414207

Domanda

Mi chiedo quale sarebbe il miglior metodo per mantenere le connessioni al database nell'applicazione .Net (ADO.NET ma immagino che la pratica dovrebbe essere la stessa per qualsiasi livello di dati). Dovrei creare una connessione al database e propagarla in tutta la mia applicazione, o sarebbe meglio passare semplicemente stringhe / fabbriche di connessione e creare una connessione ad-hoc, quando è necessario.

Come ho capito, il colpo di perfomance non è significativo con il pooling e mi permette di recuperare abbastanza facilmente da connessioni interrotte (verrà creata solo una nuova connessione) ma poi di nuovo un oggetto di connessione è una bella astrazione di livello relativamente alto e la creazione una nuova connessione per ogni operazione (non comando SQL, ma operazione dell'applicazione) genera codice duplicato aggiuntivo e sembra una perdita di tempo / risorse (?).

Cosa ne pensi di questi 2 casi, quali sono i loro contro / pro e quale approccio stai usando nelle tue applicazioni nella vita reale?

Grazie

È stato utile?

Soluzione

Mi sono ritrovato a dover passare un oggetto connessione in modo da poter consentire a diversi oggetti business di salvarsi nel database all'interno di una singola transazione.

Se ogni oggetto business dovesse creare la propria connessione SQLC al database, la transazione passerebbe a una transazione distribuita e volevo evitarlo.

Non mi è piaciuto dover passare l'oggetto SQLConnection come parametro per salvare un oggetto, quindi ho creato un ConnectionManager che gestisce la creazione dell'oggetto SQLConnection per me, tenendo traccia dell'uso dell'oggetto SQLConnection e disconnettendo l'oggetto SQLConnection quando non in uso.

Ecco un po 'di codice come esempio di ConnectionManager:

public class ConnectionManager: IDisposable
{
    private ConnectionManager instance;

    [ThreadStatic]
    private static object lockObject; 
    private static Object LockObject
    {
        get
        {
            if (lockObject == null)
                lockObject = new object();
            return lockObject;
        }
    }

    [ThreadStatic]
    private static Dictionary<string, ConnectionManager> managers;
    private static Dictionary<string, ConnectionManager> Managers
    {
        get
        {
            if (managers == null)
                managers = new Dictionary<string, ConnectionManager>();
            return managers;
        }
    }

    private SqlConnection connection = null;
    private int referenceCount;
    private string name;


    public static ConnectionManager GetManager(string connectionName)
    {
        lock (LockObject)
        {
            ConnectionManager mgr;
            if (Managers.ContainsKey(connectionName))
            {
                mgr = Managers[connectionName];
            }
            else
            {
                mgr = new ConnectionManager(connectionName);
                Managers.Add(connectionName, mgr);
            }

            mgr.AddRef();
            return mgr;
        }
    }

    private ConnectionManager(string connectionName)
    {
        name = connectionName;
        connection = new SqlConnection(GetConnectionString(connectionName));
        connection.Open();
    }

    private string GetConnectionString(string connectionName)
    {
        string conString = Configuration.ConnectionString;
        return conString; 
    }

    public SqlConnection Connection
    {
        get { return connection; }
    }

    private void AddRef()
    {
        referenceCount += 1;
    }

    private void DeRef()
    {
        lock (LockObject)
        {
            referenceCount -= 1;
            if (referenceCount == 0)
            {
                connection.Dispose();
                Managers.Remove(name);
            }
        }
    }

#region IDisposable Members

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            DeRef();
        }
    }

    ~ConnectionManager()
    {
        Dispose(false);
    }

#endregion

}

Ecco come lo userei da un oggetto business:

public void Save()
{   
    using (ConnectionManager mrg = ConnectionManager.GetManager("SQLConnectionString")
    {
        using (SQLCommand cmd = new SQLCommand)
        {
            cmd.connection = mgr.Connection
            // More ADO Code Here
        }

        _childObject.Save(); //this child object follows the same pattern with a using ConnectionManager.
    }
}

Salvo un oggetto business e tutti i suoi figli vengono salvati usando lo stesso oggetto connessione. Quando l'ambito si allontana dal genitore originale, l'istruzione using chiude la connessione.

Questo è uno schema che ho imparato da Rocky Lhotka nel suo framework CSLA.

Keith

Altri suggerimenti

Non dovresti davvero gestire questo problema da solo, poiché ci sono innumerevoli strumenti là fuori che possono farlo per te.

Se vuoi davvero farlo da solo, guarda nel modello Unit of Work dove è possibile gestire il ciclo di vita della connessione / transazione. Certamente non vuoi provare a navigare nelle acque disordinate dove ci sono connessioni aperte / chiuse in luoghi diversi.

Se decidi di consentire ai tuoi componenti di aprire direttamente le connessioni db, è probabile che il ciclo di vita della connessione sia troppo dettagliato e comporti molte connessioni aperte / chiuse per una singola operazione utente.

Il provider ADO.NET SQL Server esegue il pool di connessioni stesso. Puoi controllare le dimensioni del pool tramite MinPoolSize e MaxPoolSize nella stringa di connessione.

Una cosa di cui fare attenzione nel tuo esempio è che le app ASP.NET non dovrebbero usare l'archiviazione ThreadStatic, poiché un thread può essere riutilizzato e se non ripulisci tutti gli oggetti finisci con una connessione sospesa intorno.

In un'app ASP.NET utilizzerei invece la raccolta HttpContext.Items. Stai implementando IDisposable, ma ho visto scenari in cui gli sviluppatori dimenticano di chiamare Dispose o posizionare il codice in un blocco using.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top