Laden...

Datenbanken richtig öffnen und schließen

Erstellt von Schakal vor 17 Jahren Letzter Beitrag vor 16 Jahren 14.691 Views
S
Schakal Themenstarter:in
37 Beiträge seit 2005
vor 17 Jahren
Datenbanken richtig öffnen und schließen

Abgetrennt von Ressourcen schonen - Datenbanken richtig öffnen und schließen

Hi .Kai,

guter Beitrag, besonders für ASP-Anwendungen zu empfehlen Ich würde Ihn aber gern etwas erweitern.
Und zwar:
Es kann durchaus richtig und wesentlich performanter sein, die Sql(bzw. ODBC)-Connection Objekte nicht vom Garbage-Collector aufräumen zu lassen.
Besonders in Windows-Forms-Applikationen die viele Datenbank Operationen durchführen.
Wenn ich z.B. ein Verwaltungs-Gui oder Ähnliches führ eine bestimmte Datenbank schreibe, kann der Performance-Verlust für das ständig erneute aufbauen der Datenbankverbindungen ziemlich groß werden.
In diesem Fall ist es wahrscheinlich, oder zumindest nach meinen Erfahrungen wesentlich besser, die Connection Objekte als Instanz-Member des Forms oder der bestimmten Klasse im Verbundenen Zustand vorzuhalten, da so bei den unzähligen Datenbankzugriffen die Verbindungszeit wegfällt.
Außerdem ist es so möglich, eventuelle Exceptions, Fehler oder ähnliches direkt in Log-Tables oder ähnliches zu schreiben.
Der Speicherbedarf ist zwar nicht all zu gering, diesen kann man aber getrost in Kauf nehmen.
WICHTIG: Man muß darauf achten, dass einem Connection-Object, gleichzeitig nur eine Reader-Instanz zugeordnet ist. Es kann sehr oft wichtig sein, die Using anweisung so, oder ähnlich zu implemetieren.


// SqlConnection sqlCon ist als Instance-Member deklariert,
// und beim start initialisiert und geöffnet worden.
internal void example()
{
  using(SqlCommand cmd = new SqlCommand("select * from table where Id <=" + this.SetID.ToString(), sqlCon))
 {
    using(SqlDataReader rd = cmd.ExecuteReader())
    {
       while(rd.Read())
       {
              //hier der Code
       }
    }
  }
}
// jetzt sollte allerdings im Destructor auf das Abbauen der Verbindung geachtet werden. Sollte durch Garbage-Collection durchgeführt werden, ich achte allerdings immer darauf.
~MyClass(){
 this.sqlCon.Close();
}

P.S: keine Gewähr für Rechtschreib-Fehler 😉

  1. :rolleyes:98,5 % aller IT-Probleme werden durch Fehlfunktionen des Layer 8 ausgelöst!! :rolleyes:8)
1.274 Beiträge seit 2005
vor 17 Jahren
// jetzt sollte allerdings im Destructor auf das Abbauen der Verbindung geachtet werden. Sollte durch Garbage-Collection durchgeführt werden, ich achte allerdings immer darauf.
~MyClass(){
this.sqlCon.Close();
} 

Hallo,

ich sehe das ein bischen anders,

wer garantire dir das der GC auch wirklich läuft und die Resource freigibt?

lg
L.G.

"Das Problem kennen ist wichtiger, als die Lösung zu finden, denn die genaue Darstellung des Problems führt automatisch zur richtigen Lösung." Albert Einstein

T
512 Beiträge seit 2006
vor 17 Jahren

Also im Destruktor ist mit Sicherheit der falsche Platz, aber aus nem anderen Grund. Früher oder später wird der mit Sicherheit aufgerufen, aber die Frage ist viel mehr, ob sqlCon nicht schon vorher vom Garbage Collector behandelt wurde.
Das IDisposable Interface gibt es ja nicht, weil der Garbage Collector sonst die Resourcen nicht freigeben würde. Bei ner ordentlichen Implementation der Klasse sollte das zumindest nicht so sein. IDisposable gibt es um den Zeitpunkt der Freigabe zu kontrollieren.
Also nicht ob, sondern wann.

Die Regel lautet ja: im Destruktor (Finalizer) nicht auf managed Resources zugreifen!
Oder anders: der Garbage Collector garantiert nicht die Reihenfolge in der verlorene Objekte gelöscht werden.

Das bedeutet aber im Umkehrschluss nicht, dass der Garbage Collector keine Resourcen freigibt...

Das Close sollte im Dispose von MyClass kommen, oder in vergleichbaren Methoden, aber nie im Destruktor.

e.f.q.

Aus Falschem folgt Beliebiges

S
Schakal Themenstarter:in
37 Beiträge seit 2005
vor 17 Jahren

🤔
OK,
richtig IDisposable zu implementieren Funzt natürlich auch gut...
Aber, ist im Destructor wirklich der falsche Platz???

rufe dort auch eigentlich nur immer eine Methode der Klasse auf die die Verbindung schließt, wenn das Objekt noch im Speicher ist. Diese Methode wird dann natürlich auch in Dispose aufgerufen(falls implementiert).

aber warum: "wer garantiert dir das der GC auch wirklich läuft?"
...
Habe mir darüber bis jetzt nur gedanken gemacht, wenn ich in meinem Code unsafe bereiche und/oder DllImports verwendet habe.

Und diesen Satz check ich auch nicht so wirklich, wenn SqlCon als Instanz-Member der Klasse deklariert und initialisiert ist:
"Früher oder später wird der mit Sicherheit aufgerufen, aber die Frage ist viel mehr, ob sqlCon nicht schon vorher vom Garbage Collector behandelt wurde."
????
Die Instanz-Daten sollten doch solange das Objekt meiner Klasse besteht auch im Speicher bleiben.

Hat jemand zu diesem Thema einen Blog oder ne genauere Doku zum GC?
Wann will der denn früher meine Instanz-Member killen? Wie soll denn dann die Objektorientierte Programmierung funktionieren...? Der sollte doch erst meine Instanz-Daten anfassen, wenn diese durch andere Programmteile nicht mehr referenziert werden, oder habe ich da etwas falsch verstanden??? 🤔

Auszug aus "Programmieren mit C#" O'Reilly"
Wie Destruktoren funktionieren
Der GarbageCollector pflegt eine Liste von Objekten, die einen Destrucktor haben. Diese Liste wird jedes mal aktualisiert wenn ein Objekt erzeugt oder zerstört wird.

Wird ein Objekt auf dieser Liste zum ersten Mal eingesammelt, wird es zusammen mit anderen Objekten, die zerstört werden sollen, in eine Que gestellt. Nachdem der Destruktor ausgeführt wurde, sammelt der Garbage Collector das Objekt ein und aktualisiert die Liste"

____________ Ok ... Ich zitiere eine Zeile weiter unten auf der Seite.

".... Der Destrucktor wird vom Garbage Collector aufgerufen.Wenn Sie mit wertvollen unmanaged Ressourcen arbeiten, die Sie schließen und so schnell wie möglich verwerfen möchten, sollten Sie IDisposable implementieren."

Ich möchte doch aber erst die Verbindung schließen, wenn mein Programm ganz kurz vor dem Ende ist, nicht beim Beenden sondern am Ende vom Beenden 😉.
Gibt es noch einen späteren Zeitpunkt als den Destruktor. Habe So noch nie Probleme gehabt, wenn ich Full-Managed-Types verwendet habe...

Lasse mich da gern belehren..... ?(

  1. :rolleyes:98,5 % aller IT-Probleme werden durch Fehlfunktionen des Layer 8 ausgelöst!! :rolleyes:8)
1.373 Beiträge seit 2004
vor 17 Jahren

Original von Franknstein
Nur ein kleiner Punkt:
Exceptions sollten besser direkt am Entstehungsort behandelt werden.

Kann ich nicht so stehen lassen. Exceptions sollten behandelt werden, sobald man sie behandeln kann - und nicht früher!

Zum Finalizer: wenn eine Klasse den Finalizer implementiert, wird er früher oder später auch ausgeführt, soviel steht mal fest. Aber wenn es nicht um unverwaltete Resourcen geht, sollte man auf keinen Fall einen finalizer implementieren.

Um mal "Microsoft .NET Framework-Programmierung" zu stützen:

  • Objekte mit Finalizer benötige zwei(!) Garbage Collections und einen Aufruf des Finalizers, bevor sie zerstört sind
  • Dadurch wandern diese Objekte und referenzierte Objekte in höhere Generationen, wo sie den Speicher belasten
  • Das Anlegen der Objekte dauert länger, weil sie für den Aufruf des Finalizers registriert werden müssen.
  • Durch den Finalizer wird die Lebensdauer aller Objekte im betreffenden Objektgraphen erhöht, weshalb auch anderswo Resourcen erst später freigegeben werden
  • Man kann nicht steuern, wann der finalizer aufgerufen wird.
  • Die Aufrufreihenfolge ist durch die CLR nicht festgelegt, weshalb man im Finalizer nicht sicher auf andere verwaltete Objekte zugreifen kann.

Also: finalizer nur bei nicht-verwalteten Resourcen, bei denen die Gefahr eines Leaks besteht. Und auch dann sollte man das Dispose-Pattern anwenden um die Möglichkeit zu bieten, den Finalizer zu deaktivieren.

Grüße,
Andre

S
Schakal Themenstarter:in
37 Beiträge seit 2005
vor 17 Jahren

Danke VIZone,
das ist doch mal eine Aussage...

Mal zum Verständnis, wenn ich den Destrucktor nutze, kann es sein das ein Instanz-Member meiner Klasse schon aufgeräumt ist? Ich dachte es wird erst der Destructor meiner Klasse ausgeführt, und dann alle Instanz-Daten meines Objektes beseitigt...

Thanks, schönen Abend noch.

Jan

  1. :rolleyes:98,5 % aller IT-Probleme werden durch Fehlfunktionen des Layer 8 ausgelöst!! :rolleyes:8)
1.373 Beiträge seit 2004
vor 17 Jahren

Hallo,

Der "Destruktor" ist kein Destruktor im Sinne eines C++-Destruktors. Daher die Verwirrung.

Grüße,
Andre

B
119 Beiträge seit 2005
vor 17 Jahren

Um sich nicht dazu verleiten zu lassen Ressourcen zu schließen, weil es einfach zu viel "boilerplate code" ist, sollte man eventuell auch überlegen, ob sich das nicht abstrahieren, bzw. eher "herausziehen" lässt. Sich ständig wiederholende try-finally, bzw. using Blöcke neigen einfach dazu irgendwann vergessen oder sogar bewusst ignoriert zu werden. Folgendes Beispiel sollte ebenfalls zeigen, wie es aussehen könnte.


using System.Data;
using System.Data.Odbc;
using System.Collections.Generic;

// angelehnt an die Klasse JdbcTemplate aus dem Spring Framework
public class AdoTemplate {

    public IList<T> Query<T>(string sql, IRowMapper<T> rowMapper) {
        IList<T> result = new List<T>();

        using (OdbcConnection connection = new OdbcConnection()) {
            using (OdbcCommand command = new OdbcCommand(sql, connection)) {
                using (OdbcDataReader reader = command.ExecuteReader()) {
                    while (reader.Read()) {
                        result.Add(rowMapper.MapRow(reader));
                    }
                }
            }
        }

        return result;
    }
}

public interface IRowMapper<T> {

    T MapRow(IDataReader reader);
}

// so könnte das dann in der Anwendung aussehen
public class AccountDao {

    private AdoTemplate template = /* .. */;

    public IList<Account> GetAccounts() {
        return template.Query("SELECT * FROM Account", new AccountMapper());
    }

    private class AccountMapper : IRowMapper<Account> {

        public Account MapRow(IDataReader reader) {
            return new Account(
                (double) reader["Balance"], 
                (double) reader["CreditInterest"]);
        }
    }
}

T
512 Beiträge seit 2006
vor 17 Jahren

Original von Schakal
Danke VIZone,
das ist doch mal eine Aussage...

Mal zum Verständnis, wenn ich den Destrucktor nutze, kann es sein das ein Instanz-Member meiner Klasse schon aufgeräumt ist? Ich dachte es wird erst der Destructor meiner Klasse ausgeführt, und dann alle Instanz-Daten meines Objektes beseitigt...

Thanks, schönen Abend noch.

Jan

Der GC im .NET Framework funktioniert nicht über das Zählen von Referenzen. Es wird einfach nur erkannt: Ist das Objekt zugreifbar oder nicht.
Der GC arbeitet unter anderem ja auch mit Generationen. Eine Generation enthält kurzlebige Objekte, eine andere die langlebigen Objekte. Kurzlebige Objekte werden öfter überprüft und wandern eventuell irgendwann in die Generation der langlebigen Objekte, die dann seltener überprüft werden. Wenn jetzt ein Langlebiges Objekt also ein Member hat, das kurzlebig ist, wird wahrscheinlich erst das Kurzlebige aufgeräumt, bevor der Finalizer des langlebigen aufgerufen wird.
Einfach weil erkannt wird, dass das Kurzlebige vom Programm nicht mehr erreicht werden kann, auch wenn trotzdem noch Referenzen dazu existieren, in einem Objekt das später beseitigt wird.

e.f.q.

Aus Falschem folgt Beliebiges

S
Schakal Themenstarter:in
37 Beiträge seit 2005
vor 17 Jahren

Ja SUUUPER...

dann hab ich ja in meiner .NET-Vergangenheit viel zu viel Code geschrieben...
und sogar noch unnötige NULL-Prüfungen eingebaut... 😁
👍Auf was man nicht alles durch so einen kleinen Beitrag aufmerksam wird. 👍

8)Wollte doch eigentlich nur schreiben, dass man es bei Datenbank Anwendungen oder Eigenen DB-Connectoren(Nicht SQLServer oder MsIdentityInformationServ. Connectoren) vorziehen sollte eine Datenbankverbindung im geöffneten Zustand als Instanz-Member des Forms oder der Base-Class vorzuhalten. 8)

Danke Jungs... hätte auch weiterhin den Destruktor wie früher benutzt...

  1. :rolleyes:98,5 % aller IT-Probleme werden durch Fehlfunktionen des Layer 8 ausgelöst!! :rolleyes:8)
F
10.010 Beiträge seit 2004
vor 17 Jahren

Das mit den Destruktoren ist ja richtig, aber bitte nicht eine Connection
geöffnet als Instanzmember aufbewaren.

Das ist Resourcenverschwendung und nicht so gedacht.

M
47 Beiträge seit 2007
vor 16 Jahren

Hallo, habe mir gerade das pdf zu using durchgelesen und mal eine Verständnisfrage:
Da steht ja das das Using nach try{}finnaly{} übersetzt wird. Daraufhin habe ich meinen Code folgend geändert.


public void VokabelnLaden()
        {
            String selectSQL = "Select * FROM Vokabel WHERE AbfrDate<=? AND Stufe<6 ORDER BY Stufe DESC";

            using (OleDbConnection Connection = new OleDbConnection())
            {
                Connection.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=lernstoff.mdb";
                OleDbDataAdapter DatenAdapter = new OleDbDataAdapter();
                OleDbCommand Command = new OleDbCommand(selectSQL, Connection);

                //Command.Parameters.Add("Datum", OleDbType.DBDate);
                //Command.Parameters["Datum"].Value = DateTime.Now;
                Command.Parameters.AddWithValue("Datum", DateTime.Today);

                DatenAdapter.SelectCommand = Command;

                DatenTabelle.Clear();
                DatenAdapter.Fill(DatenTabelle);

                iAktRow = -1;
            }

Wenn ich jetzt aber den Connectionpfad zu Datenbank so verändere, das sie nicht gefunden wird, so bekomme ich beim ausführen trotzdem eine Fehlermeldung und sie wird nicht abgefangen. Wie kann den das sein, ich dachte sie würde im try{}-Block stehen?

6.862 Beiträge seit 2003
vor 16 Jahren

Try finally sorgt ja nur dafür das bei einer evtl. auftretenen Exception der Code im finally auf jeden Fall noch ausgeführt wird. Dadurch wird keinerlei Ecxeption gefangen. Des musst du weiterhin selber machen.

Baka wa shinanakya naoranai.

Mein XING Profil.

M
47 Beiträge seit 2007
vor 16 Jahren

Ah stimmt ja, da habe ich mich selbst ein wenig in die Irre geführt. Da in der PDF eine Textpassage so lautet:

da der Compiler diesen Code intern in einen try-catch-finally Block übersetzt.