Hallo 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. 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 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:
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