2009-01-05 6 views
16

Ich frage mich, was wäre die beste prectice in Bezug auf mainataining Verbindungen mit der Datenbank in. NET-Anwendung (ADO.NET aber ich denke, die Praxis sollte sein für jede Datenschicht gleich). Sollte ich eine Datenbankverbindung erstellen und diese in meiner gesamten Anwendung verbreiten, oder wäre es besser, nur Verbindungszeichenfolgen/Fabriken zu übergeben und eine Ad-hoc-Verbindung zu erstellen, wenn dies erforderlich ist.Best Practices für die gemeinsame Nutzung von IDbConnection oder Verbindungszeichenfolge/factory in Ihrem .Net Code

Wie ich verstehe Perfomance Hit ist nicht signifikant mit Pooling und es ermöglicht mir, von gebrochenen Verbindungen ziemlich einfach wiederherzustellen (nur eine neue Verbindung erstellt wird), aber dann wieder ein Verbindungsobjekt ist eine schöne, relativ hohe Abstraktion und Erstellen einer neuen Verbindung für jede Operation (nicht SQL-Befehl, sondern Anwendungs-Operation) generiert zusätzlichen, doppelten Code und fühlt sich wie eine Verschwendung von Zeit/Ressourcen (?).

Was denken Sie über diese 2 Fälle, was sind ihre Nachteile/Vorteile und welchen Ansatz verwenden Sie in Ihren realen Anwendungen?

Dank

Antwort

11

Ich musste ein Verbindungsobjekt weitergeben, damit ich mehreren Geschäftsobjekten erlauben konnte, sich innerhalb einer einzigen Transaktion in der Datenbank zu speichern.

Wenn jedes Geschäftsobjekt eine eigene SQLConnection für die Datenbank erstellen musste, würde die Transaktion zu einer verteilten Transaktion eskalieren, und das wollte ich vermeiden.

Ich wollte das SQLConnection-Objekt nicht als Parameter übergeben, um ein Objekt zu speichern. Daher habe ich einen ConnectionManager erstellt, der das SQLConnection-Objekt für mich erstellt, die Verwendung des SQLConnection-Objekts verfolgt und das SQLConnection-Objekt trennt Nicht in Gebrauch.

Hier ist ein Code als ein Beispiel für die 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 

} 

Hier ist, wie ich es von einem Business-Objekt verwenden würde:

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. 
    } 
} 

ich ein Business-Objekt und alle seine sparen Kinder werden ebenfalls mit demselben Verbindungsobjekt gespeichert. Wenn der Bereich vom ursprünglichen übergeordneten Element entfernt wird, schließt die using-Anweisung die Verbindung.

Dies ist ein Muster, das ich von Rocky Lhotka in seinem CSLA-Framework gelernt habe.

Keith

+0

Danke Keith. Ich mag Ihren Ansatz wirklich - ConnectionManager ist eine nette Schicht zwischen Verbindungen und Befehlen (Sie können sogar Ihr eigenes Pooling implementieren, wenn Sie wirklich wollen). Ich werde dieses Kapitel von CSLA lesen müssen. –

+0

Ich bin verwirrt, warum Sie möchten, dass das Manager-Mitglied ThreadStatic markiert. Jedes Mal, wenn auf den Getter von einem neuen Thread zugegriffen wird, wird eine neue Manager-Sammlung für diesen Thread erstellt, richtig? – dnewcome

+0

Ich sehe, warum Sie separate Manager für jeden Thread vom Standpunkt der Threadsicherheit wünschen. Sie möchten nicht, dass auf eine SqlConnection von einem anderen Thread als dem, der sie erstellt hat, zugegriffen werden kann. Ich denke nicht, dass es illegal wäre, aber du öffnest wirklich die Tür für unerwartete Dinge. – dnewcome

1

ADO.NET SQL Server-Anbieter hat die Verbindung selbst bündeln. Sie können die Poolgröße durch MinPoolSize und MaxPoolSize in der Verbindungszeichenfolge steuern.

1

Sie sollten dieses Problem wirklich nicht selbst behandeln, da es unzählige Tools gibt, die das für Sie erledigen können.

Wenn Sie es wirklich selbst machen wollen, dann schauen Sie in das Muster Unit of Work, wo Sie den Verbindungs-/Transaktionslebenszyklus verwalten können. Sie möchten sicherlich nicht versuchen, durch die unordentlichen Gewässer zu navigieren, wo Verbindungen an verschiedenen Orten geöffnet/geschlossen werden.

Wenn Sie entscheiden, dass Ihre Komponenten db-Verbindungen direkt öffnen, ist es wahrscheinlich, dass der Verbindungslebenszyklus zu feinkörnig ist und zu vielen offenen/geschlossenen Verbindungen für eine einzelne Benutzeroperation führt.

+0

nur hinzufügen ... einige der Tools würde NHibernate, LINQ to SQL, SubSonic und andere ... –

+0

Dank Ben Sie haben absolut Recht, dass in gängigen Szenarien die beste Option ist, einige Objekt Persistenz Framework zu verwenden . Leider hat mein Firmenprodukt nicht viele "feste" Objekte, sondern erlaubt es Benutzern, benutzerdefinierte Tabellen zu erstellen (wie MS Access) - so kann ich OPF nicht wirklich verwenden. –

0

Eine Sache von in Ihrem Beispiel, vorsichtig zu sein, ist, dass ASP.NET-Anwendungen nicht Threadstatic Speicher verwenden sollte, da ein Thread kann wieder verwendet werden, und wenn Sie alle Ihre Objekte, die Sie nicht bereinigen am Ende mit einer Verbindung rumhängen.

In einer ASP.NET-App würde ich stattdessen die HttpContext.Items-Auflistung verwenden. Sie implementieren IDisposable, aber ich habe Szenarien gesehen, bei denen Entwickler vergessen haben, Dispose aufzurufen oder den Code in einen using-Block zu stellen.

Verwandte Themen