Laden...

Eine DB-Connection richtig mit using verwenden?

Erstellt von frameworker vor 5 Jahren Letzter Beitrag vor 5 Jahren 1.561 Views
F
frameworker Themenstarter:in
7 Beiträge seit 2019
vor 5 Jahren
Eine DB-Connection richtig mit using verwenden?

verwendetes Datenbanksystem: <Postgres 10.5>

Folgendes Beispiel aus der getting starting-Ecke npgsql.org was die "using" Syntax angeht:


namespace ntest
{

	class MainClass
	{

		public static void Main(string[] aargs)
		{
			 ctest ct = new ctest();

			Console.WriteLine("{0}", rt.db.ProcessID);
		}
	}

	class ctest
	{

		public NpgsqlConnection db;
		
		public ctest()
		{
			using (db = new Npgsql.NpgsqlConnection("Host=localhost;Database=testdb")) {
				db.Open();
			}
                }
... usw.

Startet man das Programm, bekommt man bei jeglichem Zugriff auf das db-Objekt den Fehler > Fehlermeldung:

Cannot access a disposed object . Das kapier ich nicht.

Lasse ich das "using" weg und schreibe im Konstruktor sozusagen "herkömmlich":

db = new Npgsql.NpgsqlConnection("Host=localhost;Database=testdb"
db.Open();

dann klappt der Zugriff.

Irritierend ist auch die Aussage hier

"... dont declare the connection on the class ..."

Die NpgSql Seite hilft da auch nicht weiter. Wieso soll ich mein Datenbankobjekt nicht einmal für die gesamte Klasse im Konstruktor erstellen dürfen?
Und "using" wird doch intern nur auf try/catch Semantik umgestellt, oder gibt es da noch was zu beachten?

16.835 Beiträge seit 2008
vor 5 Jahren

Dein using() führt dazu, dass das Objekt disposed wird.
Siehe using-Anweisung (C#-Referenz)

Die Verwendung ist auch zu empfehlen: denn richtig eingesetzt sorgt das using dafür, dass die Verbindung sauber geschlossen und Ressourcen wieder freigegeben werden.
Nach dem Using kann das Objekt natürlich jedoch nicht mehr verwendet werden und quittiert das mit der Exception, die Du erhalten hast, wenn Du es trotzdem machst 😉

Was Du machst ist:

  • Connection erstellen
  • Connection öffnen
  • Conneciton schließen (durch den IDisposable Pattern)
    - Connection verwerfen (durch den IDisposable Pattern)
  • Connection ProcessID abfragen

Passt natürlich nicht im Sinne der Logik und knallt daher auch korrektweise.

F
frameworker Themenstarter:in
7 Beiträge seit 2019
vor 5 Jahren

Gut, verstehe ich soweit. Wenn mal so in diesem Stil arbeiten will muss innerhalb des using Keywords immer

  • DB Connection herstellen
  • select oder insert

zusammen stehen, syntaktisch gesehen. Und das innerhalb einer Methode.

Ich verstehe ehrlich gesagt nicht ganz, warum man den Code in C#/Postgres so aufblähen muss. Die DB Verbindung ist ja pooled, d.h. beim zweiten Zugriff öffnet sich wahrsch. nicht noch mal eine neue Verbindung. Trotzdem muss die DB Verbindung rein logisch wieder hergestellt werden.

Es reicht doch, dass das Objekt eine Instanzvariable dafür eröffnet. Der Destruktor macht eben ein db.close().
Und selbst wenn er das nicht macht, kommt da nicht der GC irgendwann ins Spiel?

T
2.224 Beiträge seit 2008
vor 5 Jahren

.NET hält beim aktiven Pooling die Verbindungen in einem internen Pool vor.
Entsprechend wird beim erstellen eines Connection Objekts keine neue Verbindung verwendet sonder eine aktive Verbindung aus dem Pool.
Eine Verbindung sollte man nie in einem aktiven Connection Objekt dauerhaft halten.

Wir verwenden im Drei-Schichten-Modell bei unseren Projekten für diese Operation, also Anlegen, Updaten, Lesen und Löschen von Einträgen nur kurz die Connection und lassen diese durch using auch direkt danach schließen.

Ebenfalls kann ein Connection Objekt nicht für parallele Operationen verwendet werden.
Heißt also wenn du eine Multithreading Anwendung hast, muss in jedem Thread ein eigenes Connection Objekt eine Verbindung bekommen!

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

16.835 Beiträge seit 2008
vor 5 Jahren

Und das innerhalb einer Methode.

Eben nicht. Du solltest niemals Dependencies von Hand verwalten.
Das ist ein Design"fehler".

Dafür gibt es die Dependency Injection und Inversion of Control.
Dependency Injection in ASP.NET Core
Und die Doku zeigt hier die beispielhafte Verwendung (funktioniert so in allen .NET Standard-fähigen Projekten) bzw. Configuring a DbContext: Dependency Injection

Ich verstehe ehrlich gesagt nicht ganz, warum man den Code in C#/Postgres so aufblähen muss. Die DB Verbindung ist ja pooled, d.h. beim zweiten Zugriff öffnet sich wahrsch. nicht noch mal eine neue Verbindung.

Weil .NET nicht nur einen Fall abdeckt, sondern .NET ein Framework auch für große Enterpriseprojekte ist.
Der Fokus liegt daher nicht auf Deinem Minimalbeispiel 😉

Moderne DI Provider erkennen den Disposable Pattern und räumen Deine Ressourcen automatisch auf, sofern Du entsprechendes korrekt implementiert hast.

Es reicht doch, dass das Objekt eine Instanzvariable dafür eröffnet. Der Destruktor macht eben ein db.close().

Es gibt in .NET keinen Dekonstruktor, sondern nur einen Finalizer.
Und nein; der Dispose-Pattern ist verantwortlich für das Aufräumen von Ressourcen.

Und selbst wenn er das nicht macht, kommt da nicht der GC irgendwann ins Spiel?

Auf Dein Minimalbeispiel bezogen ja; aber es gibt tatsächlich auch Dinge in solche Szenarien, wofür der GC nicht verantwortlich ist.
Daher empfiehlt es sich an den Pattern zu halten. Er ist üblich und sehr weit verbreitet in .NET.