C#, mysql open datareader
bearbeitet vonHallo Henry,
ein DB Command ist ein Highlander: Es kann nur eins geben (das gerade aktiv ist). Und ein DbDataReader ist aktiv, bis er geschlossen wird. D.h. du musst auf dem Reader Close() aufrufen oder Du musst ihn mit Dispose() verwerfen. Deswegen verwendet man die using-Anweisung: die führt den Dispose() für Dich automatisch aus, sobald der using-Block auf welchem Weg auch immer verlassen wird.
In PHP ist das per Default etwas anders - siehe [buffered mode](https://www.php.net/manual/de/mysqlinfo.concepts.buffering.php). Deswegen sieht man dort nie einen close()-Aufruf auf einem Result.
Dass auf der Connection noch ein DataReader offen ist, merkt .net erst, wenn eine neue Query ausgeführt werden soll. Deswegen fliegt die Exception beim ExecuteNonQuery Aufruf. Aber der Fehler ist früher passiert. Entweder hast Du vergessen, einen Reader zu schließen, oder du rufst die write-Methode in einer Schleife auf, wo Du einen Reader verarbeitest. Das geht nicht, dafür müsstest Du eine zweite Connection verwenden.
**Deswegen** gilt für Connections in .net eigentlich das „heiße Kartoffel“-Prinzip: Halte sie nicht länger, als Du unbedingt musst.
Also für jedes Command eine neue Connection holen?! Das ist doch viel zu langsam!
Nein. Nicht in .net. Sowohl für den Treiber von MySQL als auch für den Treiber wie auch den mysqlconnector.net Treiber ist [Connection Pooling](https://learn.microsoft.com/de-de/dotnet/framework/data/adonet/sql-server-connection-pooling) per Default aktiv, d.h. wenn Du eine Connection schließt, wird sie tatsächlich NICHT geschlossen, sondern offengehalten und in einen Pool zurückgegeben. Der nächste Open()-Aufruf muss dadurch nicht die Runde über den DB-Server fliegen, sondern bekommt aufwandsarm eine offene Connection aus dem Pool.
Das macht man in PHP normalerweise nicht so, ich weiß, aber da ist das Connection Pooling (bei mysqli persistent connections genannt) nicht implizit aktiv. Man kann es einschalten und auch da mit heißen Kartoffeln werfen. Aber das ist eine andere Geschichte und soll ein andermal erzählt werden…
Deswegen könntest Du deine write-Methode so abändern:
~~~c#
static bool write_thingy() {
...
try
{
using var connection = new MySqlConnection(connectionstring);
using var command = new MySqlCommand(sqlstring, connection);
command.ExecuteNonQuery();
}
catch (Exception e)
{
...
}
}
~~~
Damit verwenden die UPDATEs eine eigenständige Connection und können nicht mit einem Reader kollidieren. Kontrolliere trotzdem dort, wo Du die write-Methode aufrufst, ob Du vergessen hast, einen Reader abzuräumen.
Aber – using mit Semikolon, also Deklaration statt Anweisung? Hab ich nich neulich noch was anderes erzählt? Schon, aber genau hier ist der Anwendungsfall für die using-Deklaration. Andernfalls brauchst Du zwei using-Blöcke (in einem using kann man nicht zwei Typen vermischen) und es ist genau richtig, dass der Dispose am Ende des try-Blocks erfolgt.
Und warum using var command? Das hast Du bisher nirgends gemacht, glaub ich, aber tatsächlich hat das DbCommand (die abstrakte Superklasse von MySqlCommand) die IDisposable-Schnittstelle und was IDisposable ist, soll man auch disposen. Das sind Feinheiten, die ich mir vorher nicht so genau bei Dir angeschaut habe. Es schadet nicht unbedingt was, wenn man es nicht tut, aber ohne Dispose (explizit oder implizit per `using`) räumt das Command seine Ressourcen erst beim Finalize ab (das, was man in C++ und PHP den Destruktor nennt) und das findet irgendwann statt, wenn der Garbage Collector zuschlägt)
_Rolf_
--
sumpsi - posui - obstruxi
C#, mysql open datareader
bearbeitet vonHallo Henry,
ein DB Command ist ein Highlander: Es kann nur eins geben (das gerade aktiv ist). Und ein DbDataReader ist aktiv, bis er geschlossen wird. D.h. du musst auf dem Reader Close() aufrufen oder Du musst ihn mit Dispose() verwerfen. Deswegen verwendet man die using-Anweisung: die führt den Dispose() für Dich automatisch aus, sobald der using-Block auf welchem Weg auch immer verlassen wird.
In PHP ist das per Default etwas anders - siehe [buffered mode](https://www.php.net/manual/de/mysqlinfo.concepts.buffering.php). Deswegen sieht man dort nie einen close()-Aufruf auf einem Result.
Dass auf der Connection noch ein DataReader offen ist, merkt .net erst, wenn eine neue Query ausgeführt werden soll. Deswegen fliegt die Exception beim ExecuteNonQuery Aufruf. Aber der Fehler ist früher passiert. Entweder hast Du vergessen, einen Reader zu schließen, oder du rufst die write-Methode in einer Schleife auf, wo Du einen Reader verarbeitest. Das geht nicht, dafür müsstest Du eine zweite Connection verwenden.
**Deswegen** gilt für Connections in .net eigentlich das „heiße Kartoffel“-Prinzip: Halte sie nicht länger, als Du unbedingt musst.
Also für jedes Command eine neue Connection holen?! Das ist doch viel zu langsam!
Nein. Nicht in .net. Sowohl für den Treiber von MySQL als auch für den Treiber wie auch den mysqlconnector.net Treiber ist [Connection Pooling](https://learn.microsoft.com/de-de/dotnet/framework/data/adonet/sql-server-connection-pooling) per Default aktiv, d.h. wenn Du eine Connection schließt, wird sie tatsächlich NICHT geschlossen, sondern offengehalten und in einen Pool zurückgegeben. Der nächste Open()-Aufruf muss dadurch nicht die Runde über den DB-Server fliegen, sondern bekommt aufwandsarm eine offene Connection aus dem Pool.
Das macht man in PHP normalerweise nicht so, ich weiß, aber da ist das Connection Pooling (bei mysqli persistent connections genannt) nicht implizit aktiv. Man kann es einschalten und auch da mit heißen Kartoffeln werfen. Aber das ist eine andere Geschichte und soll ein andermal erzählt werden…
Deswegen könntest Du deine write-Methode so abändern:
~~~c#
static bool write_thingy() {
...
try
{
using var connection = new MySqlConnection(connectionstring);
using var command = new MySqlCommand(sqlstring, connection);
command.ExecuteNonQuery();
}
catch (Exception e)
{
...
}
}
~~~
Damit verwenden die UPDATEs eine eigenständige Connection und können nicht mit einem Reader kollidieren. Kontrolliere trotzdem dort, wo Du die write-Methode aufrufst, ob Du vergessen hast, einen Reader abzuräumen.
Aber – using mit Semikolon, also Deklaration statt Anweisung? Hab ich nich neulich noch was anderes erzählt? Schon, aber genau hier ist der Anwendungsfall für die using-Deklaration. Andernfalls brauchst Du zwei using-Blöcke (in einem using kann man nicht zwei Typen vermischen) und es ist genau richtig, dass der Dispose am Ende des try-Blocks erfolgt.
_Rolf_
--
sumpsi - posui - obstruxi