none
Design Frage SQL-Connection RRS feed

  • Question

  • Eine Frage an die Profis

    Wie sollte man in einer DB-Anwendung mit Zugriff auf den SQL-Server das Connection-Objekt definieren?
    Sollte man im Haupt-Form eine Globale Variable für eine Connection definieren und diese Variable dann den untergeordneten Form übergeben, so das die dann die Abfragen und Änderungen über das Globale Connection Objekt ausführen, oder sollte man lieber in jedem Form oder jeder Funktion ein Connection Objekt per "Using" definieren welches dann immer wieder zerstört wird. So würden ja Beispielsweise pro Stunde mehrere hundert Connection Objekte erstellt, geöffnet, geschlossen und zerstört. Wie sieht es da mit der Performance aus?

    Grüße Thomas

    dimanche 28 novembre 2010 10:17

Réponses

  • Hallo Thomas,

    das Verbindungspooling erfolgt je Verbindungszeichenfolge, siehe SQL Server-Verbindungspooling (ADO.NET)

    Und man sollte das Verbindungspooling immer nutzen, solange keine sehr konkreten Gründe dagegeben sprechen.
    So vermeidet man ein unnötiges Aufrechterhalten der Verbindung, wenn eine Anwendung für eine längere Zeit ruht.
    Was häufiger i. a. relativ häufig der Fall ist. Denn Menschen sind nicht so schnell und Bearbeitungsschritte
    an einem Formular etc. können einige Zeit dauern sowie durch andere Tätigkeiten unterbrochen werden.

    Werden wiederum Aktionen kurz hintereinander durchgeführt (z. B. Abrufe oder Änderungen an unterschiedlichen Tabellen)
    so wird die Verbindung erneut genutzt.
    Nebenbei fängt das Verbindungspooling einige Fehler ab, denn eine Verbindung kann neben offen
    und geschlossen auch "unbenutzbar" werden, wenn ein Verbindungsabbruch, schwerer Fehler auftritt -
    was i. a. aber erst erkannt werden kann, wenn die (Netzwerk-)Verbindung genutzt wird.
    Etwas worauf ansonsten die Anwendung hin programmiert wird.

    Was den gezeigten Code angeht: Du kannst Dir das Leben deutlich vereinfachen
    und den Code leichter lesbar gestalten, indem Du die Using Anweisung verwendest, z. B.:

        Try
          Using connection As New SqlConnection(My.Settings.EinConnectionString)
            connection.Open()
    
            Dim command As New SqlCommand("SELECT ID, Daten FROM dbo.Tabelle WHERE ID = @ID", connection)
            command.Parameters.Add(New SqlParameter("@ID", SqlDbType.Int)).Value = 4711
    
            Using reader As SqlDataReader = command.ExecuteReader(CommandBehavior.SequentialAccess)
              Do While reader.Read()
                Console.WriteLine("ID: {0}, Daten: {1}", reader.GetInt32(0), reader.GetString(1))
              Loop
            End Using
          End Using
        Catch ex As SqlException
          MessageBox.Show("Ausnahme aufgetreten: " & ex.Message)
        End Try
    
    
    Denn die SqlClient Klassen implementieren durchweg die IDisposable Schnittstelle.
    Und das Freigeben einer SqlConnection (oder eines SqlDataReader usw.) entspricht dem Schliessen derselben via Close.

    Was nun das Nutzen einer Verbindung über mehrere Methoden hinweg angeht:
    Grundsätzlich sollte man Datenzugriffscode in eigenen Datenzugriffs-Klassen zusammenfassen
    (und nicht wie in Deinem Beispiel im Formular selbst einstreuen).
    Was vor allem der besseren Übersicht und Wartbarkeit dient, siehe Schichtenarchitektur
    (und die typisierten DataSets mit ihren TableAdaptern zeigen, wie man es machen kann,
    sofern man sie nicht direkt einsetzt)
    Was so nebenbei das Verwalten einer Verbindung über mehrere Methoden hinweg erleichtert.

    Gruß Elmar

     

    • Marqué comme réponse tommytom73 lundi 29 novembre 2010 22:04
    lundi 29 novembre 2010 10:27
    Auteur de réponse

Toutes les réponses

  • Hallo,

     

    ich persönlich bin der Meinung, schließen statt offen lassen. Eine Verbindung, sollte immer nur so lange offen gehalten werden, wie sie benötigt wird. Damit würde ich auch das Object wieder schließen. Denn in .NET zb wird die Connection nicht jedes mal wieder neu geöffnet. Wenn man die Connection schließt kommt das so genannte Connection Pooling zum einsatz. Hier wird die Connection sozusagen abgelegt, damit sie nicht jedesmal komplett neu aufgebaut werden muss.

    Ich denke es kommt auch ein bisschen auf die Sprache an, mit der du arbeitest, aber je länger du eine Connection un die dazugehörigen Objekte aufhälst, desto mehr blockierst du diese Objekte für eventuelle eintretende Aufrufe.In so ziemlich jedem Buch was ich bisher gelesen habe steht immer, man solle die Connection zur DB so schnell wie möglich wieder schließen, da die Arbeit mit den Daten offline viel schneller ist.


    Cheers Jörn Bosse, Microsoft Junior Studentpartner
    dimanche 28 novembre 2010 11:54
  • Ja das habe ich auch so gelesen, aber ich meine das Connection Objekt jedes mal zu zerstören.

    Bsp. Global

    Public Class FrmMain as Form
        Inherits Form
        '//
        Friend _ObjSqlConnection as SqlClient.SqlConnection
        '//
    .....
        Private Sub FormLoad(sender as Object, e as ........) handles .......
        '//
        Try
            Me._ObjSqlConnection as New SqlClient.SqlConnection(ConStr)
            Me._ObjSqlConnection.open()
                ..... Abfragen ausführen
            Me._ObjSqlConnection.close()
        Catch ex as SQLException
           msgbox(Fehlermeldung)
        end Try
        '//
        End Sub

       Private Sub IrgendEineAndereMethode()
       '//
       try
           Me._ObjSqlConnection.open()
                ..... Abfragen ausführen
           Me._ObjSqlConnection.close()
       Catch ex as SQLException
           msgbox(Fehlermeldung)
       End Try
       '//
       End Sub
    .....
    End Class

    oder jedes mal Anlegen und Zerstören

    Public Class FrmMain as Form
        Inherits Form
        '//
    .....
        Private Sub FormLoad(sender as Object, e as ........) handles .......
        '//
        Dim tmpConnection as SqlClient.SqlConnection
        '//
        Using tmpConnection = New SqlClient.SqlConnection(ConStr)
           '//
           tmpConnection.open()
                ..... Abfragen ausführen
           tmpConnection.close
        Catch ex as SqlException
           msgbox(Fehlermeldung)
        end Try
        '//
        End Sub

       Private Sub IrgendEineAndereMethode()
        '//
        Dim tmpConnection as SqlClient.SqlConnection
        '//
        Using tmpConnection = New SqlClient.SqlConnection(ConStr)
           '//
           tmpConnection.open()
                ..... Abfragen ausführen
           tmpConnection.close
        Catch ex as SqlException
           msgbox(Fehlermeldung)
        end Try
        '//
       End Sub
    .....
    End Class


    lt.den Büchern die ich gelesen habe wird das Connection Objekt "tmpConnection" beim beenden der Methode oder Funktion auch immer zerstört und implizit Dispose aufgerufen.

    Gruß Thomas

    dimanche 28 novembre 2010 12:20
  • Hallo Thomas,

    Insofern du das Objekt in jeder Sub im Form benötigst und diese sich nicht gegenseitig aufrufen, dann kannst du das Objekt global erzeugen. Würdest du aber in Sub 1 das Objekt verwenden und in Sub2 auch und in Sub1 Sub2 aufrufen, so würde es probleme geben.

    Du musst also genau wissen was du machen willst. Aber natürlich ist es sinnvoller das Objekt, solange am Leben zu erhalten, wie du es benötigst. Dir sollte dabei aber auch klar sein, dass selbst ein 1000 faches Erzeugen des Objektes in der Stunde weniger Ressourcen verbraucht, wie ein, über diesen Zeitraum hinweg, ständig lebendes Objekt.

    Du kannt aber auch nochmal hier nachsehen: http://openbook.galileocomputing.de/visualbasic_2008/

    Ab Seite 362 findest du nochmals Infos zur Lebensdauer von Objekten.


    Cheers Jörn Bosse, Microsoft Junior Studentpartner
    dimanche 28 novembre 2010 13:05
  • Hallo Thomas,

    Insofern du das Objekt in jeder Sub im Form benötigst und diese sich nicht gegenseitig aufrufen, dann kannst du das Objekt global erzeugen. Würdest du aber in Sub 1 das Objekt verwenden und in Sub2 auch und in Sub1 Sub2 aufrufen, so würde es probleme geben.

    Pro "Form" würde ich das Objekt nur einmal aufrufen. Und man müsste jedes mal den Verbindungsstatus prüfen.

    Du musst also genau wissen was du machen willst. Aber natürlich ist es sinnvoller das Objekt, solange am Leben zu erhalten, wie du es benötigst. Dir sollte dabei aber auch klar sein, dass selbst ein 1000 faches Erzeugen des Objektes in der Stunde weniger Ressourcen verbraucht, wie ein, über diesen Zeitraum hinweg, ständig lebendes Objekt.

    Du kannt aber auch nochmal hier nachsehen: http://openbook.galileocomputing.de/visualbasic_2008/

    Ab Seite 362 findest du nochmals Infos zur Lebensdauer von Objekten.


    Cheers Jörn Bosse, Microsoft Junior Studentpartner

    Funktioniert das Verbindungspolling Pro Anwendung ? Ich meine werden alle Conection Objekte die ich in der Anwendung erzeuge, egeal auf welcher Form oder in welcher Methode/Funktion dem Verbindungspolling zugeordnet? Wenn das so ist, wäre es wirklich egal wenn ich die mit "Using" erzeugte Connection die nach beendigung des "Using" zerstört wird benutzen würde. Hintergrund ist der das wir planen, ein DB-Anwendung zu machen, wo der SQL-Server nur über eine gesicherte VPN-Verbindung über das Internet erreichbar ist.
    dimanche 28 novembre 2010 18:53
  • Hallo,

    ich persönlich bin der Meinung, schließen statt offen lassen.

    Warum?

    Eine Verbindung, sollte immer nur so lange offen gehalten
    werden, wie sie benötigt wird.

    Das hiesse also für eine Anwendung bei der ständig, vom Start
    bis zum Ende, auf die DB zugegriffen wird, die Verbindung
    ständig offfen halten. Ein Widerspruch zu oben.

    Damit würde ich auch das Object wieder schließen.
    Denn in .NET zb wird die Connection nicht jedes mal
    wieder neu geöffnet. Wenn man die Connection schließt
    kommt das so genannte Connection Pooling zum einsatz.

    Wobei man dann keine 100 prozentige Kontrolle darüber
    hat, wann welche Connection tatsächlich geschlossen wird
    und später wieder neu geöffnet werden muss.

    Hier wird die Connection sozusagen abgelegt, damit
    sie nicht jedesmal komplett neu aufgebaut werden muss.

    Jede geöffnete Connection ist aber für den Server ein
    Benutzer und muss vom Server mit mehr oder weniger
    Aufwand verwaltet werden. Es können somit selbst dann
    Mehrbenutzerkonflikte auftreten, wenn eigentlich nur eine
    Anwendung (über mehrere Connections) auf den Server
    zugreift.

    Ich denke es kommt auch ein bisschen auf die Sprache an,
    mit der du arbeitest,

    Woher sollte der SQL-Server wissen, in welcher Sprache
    irgendein zugreifendes Clientprogramm geschrieben ist?

    aber je länger du eine Connection un die dazugehörigen
    Objekte aufhälst, desto mehr blockierst du diese Objekte
    für eventuelle eintretende Aufrufe.

    Wann und warum ist eine geöffnete Connection oder
    zugehörige Objekte (welche Objekte?) für welche
    Aufrufe blockiert?

    In so ziemlich jedem Buch was ich bisher gelesen habe
    steht immer, man solle die Connection zur DB so schnell
    wie möglich wieder schließen,

    Tatsächlich findet man dies in vielen Büchern, aber eine
    schlüssige Erklärung hierfür habe ich bisher in keinem
    dieser Bücher gefunden.

    da die Arbeit mit den Daten offline viel schneller ist.

    Eine geöffnete Connection verhindert doch nicht die
    Arbeit mit Daten, die beim Client z.B. lokal in DataTables
    eines DataSets, Recordsets oder vergleichbaren Datenspeichern
    liegen. Was also hat der Zustand eines Connectionobjektes
    (geöffnet oder geschlossen) mit der Verarbeitung lokaler
    (offline) Daten zu tun?

    Gruß aus St.Georgen
    Peter Götz
    www.gssg.de (mit VB-Tipps u. Beispielprogrammen)

    lundi 29 novembre 2010 07:39
  • Hallo,

    Pro "Form" würde ich das Objekt nur einmal aufrufen.
    Und man müsste jedes mal den Verbindungsstatus prüfen.

    Was meinst Du mit "Verbindungsstatus prüfen"?
    Eine Connection kann geöffnet oder geschlossen sein.
    Wenn man sie im eigenen Programmcode selbst
    geöffnet oder geschlossen hat, kennt man zwangsläufig
    auf diesen Zustand.

    ... schnipp ...

    Funktioniert das Verbindungspolling Pro Anwendung ?

    Nicht Verbindungspolling (zyklisches Abfragen) sondern
    Verbindungspooling (Verwalten mehrerer Verbindungen
    in einem Pool / Vorratsspeicher).

    Ich meine werden alle Conection Objekte die ich in der
    Anwendung erzeuge, egeal auf welcher Form oder in welcher
    Methode/Funktion dem Verbindungspolling zugeordnet?

    Bei .net ist Connectionpooling = Ja die Standardeinstellung
    für ein Connectionobjekt.

    Gruß aus St.Georgen
    Peter Götz
    www.gssg.de (mit VB-Tipps u. Beispielprogrammen)

    lundi 29 novembre 2010 07:54
  • Hallo Jörn,

    Insofern du das Objekt in jeder Sub im Form benötigst und
    diese sich nicht gegenseitig aufrufen, dann kannst du das
    Objekt global erzeugen.

    Warum sollen verschiedene Forms, die sich gegenseitig
    aufrufen, nicht das selbe Connectionobjekt nutzen können?

    Würdest du aber in Sub 1 das Objekt verwenden und
    in Sub2 auch und in Sub1 Sub2 aufrufen,

    ... dann würden Sub1 und Sub2 zeitlich hintereinander
    abgearbeitet, sofern diese nicht explizit in verschiedenen,
    parallel ablaufenden Threads  abgearbeitet würden.

    so würde es probleme geben.

    Welche konkreten Probleme?

    Du musst also genau wissen was du machen willst.

    Na ja, das sollte man eigentlich immer, wenn man
    ein wie auch immer geartetes Programm erstellt.

    Aber natürlich ist es sinnvoller das Objekt, solange am
    Leben zu erhalten, wie du es benötigst.

    Also bei Programmen, die ständig wiederkehrend in
    kurzen Abständen immer wieder auf die DB zugreifen,
    vom Programmstart bis zum Programmende?

    Dir sollte dabei aber auch klar sein, dass selbst ein 1000
     faches Erzeugen des Objektes in der Stunde weniger
    Ressourcen verbraucht, wie ein, über diesen Zeitraum
    hinweg, ständig lebendes Objekt.

    Jedes geöffnete Connectionobjekt wird vom Server als
    ein Benutzer gesehen. Ich denke es macht schon einen
    Unterschied, ob dieser Server einen Benutzer oder
    deren eintausend zu verwalten hat.

    Gruß aus St.Georgen
    Peter Götz
    www.gssg.de (mit VB-Tipps u. Beispielprogrammen)

    lundi 29 novembre 2010 08:09
  • Hallo Thomas,

    das Verbindungspooling erfolgt je Verbindungszeichenfolge, siehe SQL Server-Verbindungspooling (ADO.NET)

    Und man sollte das Verbindungspooling immer nutzen, solange keine sehr konkreten Gründe dagegeben sprechen.
    So vermeidet man ein unnötiges Aufrechterhalten der Verbindung, wenn eine Anwendung für eine längere Zeit ruht.
    Was häufiger i. a. relativ häufig der Fall ist. Denn Menschen sind nicht so schnell und Bearbeitungsschritte
    an einem Formular etc. können einige Zeit dauern sowie durch andere Tätigkeiten unterbrochen werden.

    Werden wiederum Aktionen kurz hintereinander durchgeführt (z. B. Abrufe oder Änderungen an unterschiedlichen Tabellen)
    so wird die Verbindung erneut genutzt.
    Nebenbei fängt das Verbindungspooling einige Fehler ab, denn eine Verbindung kann neben offen
    und geschlossen auch "unbenutzbar" werden, wenn ein Verbindungsabbruch, schwerer Fehler auftritt -
    was i. a. aber erst erkannt werden kann, wenn die (Netzwerk-)Verbindung genutzt wird.
    Etwas worauf ansonsten die Anwendung hin programmiert wird.

    Was den gezeigten Code angeht: Du kannst Dir das Leben deutlich vereinfachen
    und den Code leichter lesbar gestalten, indem Du die Using Anweisung verwendest, z. B.:

        Try
          Using connection As New SqlConnection(My.Settings.EinConnectionString)
            connection.Open()
    
            Dim command As New SqlCommand("SELECT ID, Daten FROM dbo.Tabelle WHERE ID = @ID", connection)
            command.Parameters.Add(New SqlParameter("@ID", SqlDbType.Int)).Value = 4711
    
            Using reader As SqlDataReader = command.ExecuteReader(CommandBehavior.SequentialAccess)
              Do While reader.Read()
                Console.WriteLine("ID: {0}, Daten: {1}", reader.GetInt32(0), reader.GetString(1))
              Loop
            End Using
          End Using
        Catch ex As SqlException
          MessageBox.Show("Ausnahme aufgetreten: " & ex.Message)
        End Try
    
    
    Denn die SqlClient Klassen implementieren durchweg die IDisposable Schnittstelle.
    Und das Freigeben einer SqlConnection (oder eines SqlDataReader usw.) entspricht dem Schliessen derselben via Close.

    Was nun das Nutzen einer Verbindung über mehrere Methoden hinweg angeht:
    Grundsätzlich sollte man Datenzugriffscode in eigenen Datenzugriffs-Klassen zusammenfassen
    (und nicht wie in Deinem Beispiel im Formular selbst einstreuen).
    Was vor allem der besseren Übersicht und Wartbarkeit dient, siehe Schichtenarchitektur
    (und die typisierten DataSets mit ihren TableAdaptern zeigen, wie man es machen kann,
    sofern man sie nicht direkt einsetzt)
    Was so nebenbei das Verwalten einer Verbindung über mehrere Methoden hinweg erleichtert.

    Gruß Elmar

     

    • Marqué comme réponse tommytom73 lundi 29 novembre 2010 22:04
    lundi 29 novembre 2010 10:27
    Auteur de réponse
  • Danke euch Jörn, Peter und Elmar

    Ihr habt mir erstmal geholfen mein Konzept zu überdenken.  Wenn ich noch Fragen habe weiß ich ja wo ich mal fragen kann :-)

    Gruß Thomas

    lundi 29 novembre 2010 22:09
  • Hallo Thomas
     
    Auch wenn Du die Connection schliesst, wird diese nicht unbedingt freigegeben, sondern wandert wieder in den Pool der Connections und wird dann später wieder weiterverwendet. Dort werden diese vom Pooler verwaltet, der die physische Verbindung aufrecht erhält. Wenn Du eine neue Verbindung öffnest, schaut der Pooler im Pool nach, ob da eine vorhanden ist und gibt Dir diese zurück, ohne eine neue zu öffnen.
    Das funktioniert nur, wenn Du die Connection auch schliesst, wenn Du diese nicht mehr brauchst.
    Im MSDN findest Du dazu einige Artikel, z.B. diesen für das .NET Framework 4 "SQL Server Connection Pooling (ADO.NET)": http://msdn.microsoft.com/en-us/library/8xx3tyca.aspx
     
    HTH
    Henry
     
     

    Eine Frage an die Profis

    Wie sollte man in einer DB-Anwendung mit Zugriff auf den SQL-Server das Connection-Objekt definieren?
    Sollte man im Haupt-Form eine Globale Variable für eine Connection definieren und diese Variable dann den untergeordneten Form übergeben, so das die dann die Abfragen und �?nderungen über das Globale Connection Objekt ausführen, oder sollte man lieber in jedem Form oder jeder Funktion ein Connection Objekt per "Using" definieren welches dann immer wieder zerstört wird. So würden ja Beispielsweise pro Stunde mehrere hundert Connection Objekte erstellt, geöffnet, geschlossen und zerstört. Wie sieht es da mit der Performance aus?

    Grü�?e Thomas

    mardi 30 novembre 2010 09:53
  • Hallo Henry

    Mit schließen habe ich ja kein problem. Bei mir werden alle Connection.open Anweisungen auch mit Connection.close geschlossen. Die Frage die sich mir stellte war Connection.dispose

    Wenn ich in einer Funktion mit Using eine Connection erstelle (Mit immer dem gleichen Connection String) wird ja immer beim Verlassen des Using-Blocks implizip Connection.dispose aufgerufen. Und da stellte sich mir die Frage, ob das immer neu erstellen des Connection Objekt Vorteilhaft ist oder nicht.

    Gruß Thomas

    mardi 30 novembre 2010 19:11
  • Hallo Thomas
     
    Ich bin nicht sicher, ob der Dispose überhaupt ausgeführt wird. Ich vermute eher, der wird ignoriert.
     
    Gruss
    Henry

    Hallo Henry

    Mit schlie�?en habe ich ja kein problem. Bei mir werden alle Connection.open Anweisungen auch mit Connection.close geschlossen. Die Frage die sich mir stellte war Connection.dispose

    Wenn ich in einer Funktion mit Using eine Connection erstelle (Mit immer dem gleichen Connection String) wird ja immer beim Verlassen des Using-Blocks implizip Connection.dispose aufgerufen. Und da stellte sich mir die Frage, ob das immer neu erstellen des Connection Objekt Vorteilhaft ist oder nicht.

    Gru�? Thomas

    jeudi 2 décembre 2010 10:17
  • Hallo Henry,

    Dispose und Close sind bei einer SqlConnection gleichwertig (Dispose ruft Close auf).
    Beides führt dazu, dass die Verbindung an den Connection Pool zurückgegeben wird.

    Gruß Elmar

    jeudi 2 décembre 2010 10:38
    Auteur de réponse
  • Hallo Elmar
    Danke für die Klarstellung. Macht also keinen Sinn nach dem Close einen Dispose aufzurufen, weil dieser sowieso nur wieder Closen würde.
    Gruss
    Henry

    Dispose und Close sind bei einer SqlConnection gleichwertig (Dispose ruft Close auf).
    Beides führt dazu, dass die Verbindung an den Connection Pool zurückgegeben wird.

    jeudi 2 décembre 2010 10:55