Entwickler-Ecke

Datenbanken (inkl. ADO.NET) - DataTable mit einer Anweisung nach MySQL


Talemantros - Di 24.05.16 20:12
Titel: DataTable mit einer Anweisung nach MySQL
Hallo,
Ich habe eine BindingSource deren Daten ich gern in meine MySQL Datenbank schreiben würde.

Derzeit würde ich die Daten der BindingSource in eine DataTable schreiben.
Ist es möglich diese ohne jede Zeile durchzugehen in einem "Rutsch" im eine Table der Datenbank zu schreiben?

Die Daten der BindingSource kommen über die Eingabe des Users.
Danke

Gruß

Edit:
Das ich die Daten von der BindingSource in das DataTable schreiben würde meine ich so:

C#-Quelltext
1:
dtRechnung = (bsUebersicht.DataSource as DataTable).Clone();                    


Ralf Jansen - Di 24.05.16 21:55

Die BindingSource hat keine Daten. Sie verweist auf Daten die an der DataSource hängen und laut deinem Code wo du einfach das Object auf eine DataTable castest ist da schon eine DataTable also gar kein Grund irgendwas in eine DataTable zu schreiben die Daten sind schon in einer DataTable.

Um den Inhalt einer DataTable mit einer Datenbank zu synchronisieren (also Änderungen in der DataTable als Updates,Inserts,Deltetes an die DB zu senden) benutzt man einen DataAdapter.
Guckst du z.B. hier [http://openbook.rheinwerk-verlag.de/visual_csharp_2012/1997_33_001.html#dodtp3e44ce92-e1cb-4420-9294-a993f673d1e4] einschließlich der beiden folgenden Kapitel. Das bezieht sich zwar auf den SqlServer das Konstrukt aus DataAdapter und Konsorten ist aber immer gleich bzw. zumindest ähnlich. Anstatt SqlDataAdapter heißt es dann bei dir vermutlich MySqlDataAdapter und so weiter. Falls es relevante Unterschiede gibt sollten die in der Doku zu deinen Zugriffskomponenten für MySql beschrieben sein.


Talemantros - Do 02.06.16 19:46

Hallo Ralf,
ich habe die entsprechenden Artikel gelesen und auch vieles verstanden.
Für das was mir noch unklar ist würde ich (weíl es glaube hier nicht rein passt) ein neues Thema aufmachen.

Was mir in Zusammenhang zu diesem Thema nicht ganz klar ist:
Wenn ich die Kapitel alle richtig gelesen und verstanden habe dann gehen die immer darum, dass man mit dem DataAdapter die OriginalTabelle updaten kann.

Habe ich das missverstanden oder müsste ich dann nach Kapitel 35.2 "Manuell gesteuerte Aktualisierung" machen!?

Was mir dann wider rum nicht ganz klar wäre:

Wenn die UI die Daten über eine andere "Schicht" der Software anfordert und die dazugehörige Methode eine DataTable zurück liefert, welches im DataGridView (z.B.:) angezeigt würde, woher weiß dann die neue Methode, die dies Updaten soll den "alten" DataAdapter einer anderen Methode,da ich diesen lt. Kapitel ja dem SQLBuilder als Parameter mit geben müsste.

Vielen Dank


Ralf Jansen - Do 02.06.16 20:05

Zitat:
Wenn ich die Kapitel alle richtig gelesen und verstanden habe dann gehen die immer darum, dass man mit dem DataAdapter die OriginalTabelle updaten kann.

Habe ich das missverstanden oder müsste ich dann nach Kapitel 35.2 "Manuell gesteuerte Aktualisierung" machen!?

Wenn du das Zeug in die Datenbank zurückschreiben willst und es zu kompliziert ist um automatisch generierte Updates/Insert/Delete zu benutzen und die selber machen willst ja.

Zitat:
Wenn die UI die Daten über eine andere "Schicht" der Software anfordert und die dazugehörige Methode eine DataTable zurück liefert, welches im DataGridView (z.B.:) angezeigt würde, woher weiß dann die neue Methode, die dies Updaten soll den "alten" DataAdapter einer anderen Methode,da ich diesen lt. Kapitel ja dem SQLBuilder als Parameter mit geben müsste.


Du brauchst den alten DataAdapter nicht. Du brauchst nur einen DataAdapter mit den passenden Statement zum richtigen Zeitpunkt.
Zum Beispiel kannst du zum lesen einen DataAdapter nehmen der nur ein SelectStatement hat oder hier das mit dem DataAdapter einfach lassen und direkt das Select Statement ausführen. Zum zurückschreiben kannst du dann einfach einen neuen erzeugen. Du musst an dem nur die Insert/Update/Delete Statements passen setzen.


Talemantros - So 05.06.16 00:02

Hey.
habe mich jetzt mal daran verscuht und wie sollte es anders sein, leider ohne Erfolg.

Ich übergebe ein DataTable an die Methode.
Interessanterweise kommt kein Fehler e läuft sauber durch, schreibt aber nicht die Daten.

Habt ihr eine Idee für mich?


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
        private static void SetDataTableParameterOfTemplate(DataTable myTable)
        {
            using (MySqlConnection conn = new MySqlConnection(connStr))
            {
                using (MySqlCommand cmd = new MySqlCommand())
                {
                    cmd.Connection = conn;
                    using (MySqlDataAdapter da = new MySqlDataAdapter (cmd))
                    {
                        using (MySqlCommandBuilder cmb = new MySqlCommandBuilder(da))
                        {
                            da.InsertCommand = CreateInsertCommand(conn);
                            da.Update(myTable);
                        }
                    }
                }
            }
        }


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
        static MySqlCommand CreateInsertCommand(MySqlConnection conn)
        {
            strSQl = @"INSERT INTO vorlagendetailalt (rechnungid, sprungmarke, detail, inhalt, vonSpalte, bisSpalte, extra, abstand, merge, 
                        aligment, border, schriftart, schriftgroesse) VALUES (?rechnungid, ?sprungmarke, ?detail, ?inhalt, ?vonSpalte, 
                        ?bisSpalte, ?extra, ?abstand, ?merge, ?aligment, ?border, ?schriftart, ?schriftgroesse)"
;

            MySqlCommand cmd = new MySqlCommand(strSQl, conn);
                cmd.Parameters.AddWithValue("?rechnungid""rechnungid");
                cmd.Parameters.AddWithValue("?sprungmarke""sprungmarke");
                cmd.Parameters.AddWithValue("?detail""detail");
                cmd.Parameters.AddWithValue("?inhalt""inhalt");
                cmd.Parameters.AddWithValue("?vonSpalte""vonSpalte");
                cmd.Parameters.AddWithValue("?bisSpalte""bisSpalte");
                cmd.Parameters.AddWithValue("?extra""exra");
                cmd.Parameters.AddWithValue("?abstand""abstand");
                cmd.Parameters.AddWithValue("?merge""merge");
                cmd.Parameters.AddWithValue("?aligment""aligment");
                cmd.Parameters.AddWithValue("?border""border");
                cmd.Parameters.AddWithValue("?schriftart""schriftart");
                cmd.Parameters.AddWithValue("?schriftgroesse""schriftgroesse");
            return cmd;
        }


Beim Paramter denke ich muss ich den Spaltennamen der DataTable übergeben, wenn ich das richtig verstanden habe


Th69 - So 05.06.16 08:19

Du verwendest die falsche Add-Methode (der Value wird ja erst zur Laufzeit hinzugefügt), s.a. Beispiel in SqlDataAdapter.InsertCommand-Eigenschaft [https://msdn.microsoft.com/de-de/library/system.data.sqlclient.sqldataadapter.insertcommand%28v=vs.110%29.aspx], d.h. verwende die SqlParameterCollection.Add-Methode (String, SqlDbType, Int32, String) [https://msdn.microsoft.com/de-de/library/e5xwx8sc%28v=vs.110%29.aspx].

Hier noch ein Beispiel: Insert command with parameters: SqlDataAdapter [http://www.java2s.com/Tutorial/CSharp/0560__ADO.Net/Insertcommandwithparameters.htm]

Wobei ich denke, daß dieses manuelle Erzeugen eines Insert-Commands nicht die beste Lösung ist. Ich nutze meistens den Standardweg per Select-Command (aus dem dann intern die anderen Commands erzeugt werden) - (bzw. verwende ein ORM ;- ).


Ralf Jansen - So 05.06.16 10:29

Du schiebst denn DataAdapter durch einen CommandBuilder UND versuchst das Insert Sql selbst zu erzeugen. Du solltest das Sql durch den CommandBuilder erzeugen lassen ODER das selbst machen.
Um die passenden Sqls zu erzeugen brauchst du für den MySqlCommandBuilder zumindest das Select Statement.


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
string selectSQL = @"SELECT rechnungid, sprungmarke, detail, inhalt, vonSpalte, bisSpalte, extra, abstand, merge, aligment, border, schriftart, schriftgroesse
                       FROM vorlagendetailalt"


using (MySqlConnection conn = new MySqlConnection(connStr))
{
    conn.Open();
    using (MySqlCommand cmd = new MySqlCommand(selectSQL, conn))
        using (MySqlDataAdapter da = new MySqlDataAdapter (cmd))
            using (MySqlCommandBuilder cmb = new MySqlCommandBuilder(da))
                da.Update(myTable);
}
myTable.AcceptChanges();


Den InsertCommand Command selbst schreiben würde ich nur machen wenn das Select Statement so kompliziert ist das es dazu kein passendes Insert Statement gibt. Z.B. Weil es ein join mit mehreren Tabellen ist, berechnete Spalten enthält, ein gruppierte Abfrage ist, weil es zum speichern ein Rechte Konzept zum schreiben über Stored Procedures gibt .....


Talemantros - So 05.06.16 10:47

Hallo ihr beiden,
aufgrund eurer Erklärungen würde ich trotzdem dazu tendieren den Insert selber zu schreiben, da zum einen ich die Daten die nach vorlagendetailalt sollen nicht aus der Tabelle kommen und zum anderen die Abfrage aus einigen Joins bestehen würde, bzw. ich das DataTable nachträglich noch eine Spalte hinzufüge.

Ich habe nun erstmal nach Th69 Anweisung folgendes geändert, was aber derzeit noch nicht funktioniert

C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
        static MySqlCommand CreateInsertCommand(MySqlConnection conn)
        {
            strSQl = @"INSERT INTO vorlagendetailalt (rechnungid, sprungmarke, detail, inhalt, vonSpalte, bisSpalte, extra, abstand, merge, 
                        aligment, border, schriftart, schriftgroesse) VALUES (?rechnungid, ?sprungmarke, ?detail, ?inhalt, ?vonSpalte, 
                        ?bisSpalte, ?extra, ?abstand, ?merge, ?aligment, ?border, ?schriftart, ?schriftgroesse)"
;

            MySqlCommand cmd = new MySqlCommand(strSQl, conn);
                cmd.Parameters.Add("?rechnungid", MySqlDbType.Int64 , 5 , "rechnungid");
                cmd.Parameters.Add("?sprungmarke", MySqlDbType.VarChar, 100,  "sprungmarke");
                cmd.Parameters.Add("?detail", MySqlDbType.VarChar, 50"detail");
                cmd.Parameters.Add("?inhalt", MySqlDbType.VarChar, 300"inhalt");
                cmd.Parameters.Add("?vonSpalte", MySqlDbType.Int16, 4"vonSpalte");
                cmd.Parameters.Add("?bisSpalte", MySqlDbType.Int16, 4"bisSpalte");
                cmd.Parameters.Add("?extra",MySqlDbType.VarChar, 50"exra");
                cmd.Parameters.Add("?abstand", MySqlDbType.Int16, 4"abstand");
                cmd.Parameters.Add("?merge", MySqlDbType.Int16, 4"merge");
                cmd.Parameters.Add("?aligment", MySqlDbType.VarChar, 50"aligment");
                cmd.Parameters.Add("?border", MySqlDbType.VarChar, 50"border");
                cmd.Parameters.Add("?schriftart", MySqlDbType.VarChar, 50"schriftart");
                cmd.Parameters.Add("?schriftgroesse", MySqlDbType.VarChar, 50"schriftgroesse");
            return cmd;
        }


Jetzt muss ich mir noch mal die Änderung von Ralf anschauen und gucken ob ich die umgesetzt bekommme
Danke schonmal

Edit:
Habe es nun wie folgt, aber es schreibt einfach keine Daten


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
        private static void SetDataTableParameterOfTemplate(DataTable myTable)
        {
            using (MySqlConnection conn = new MySqlConnection(connStr))
            {
                using (MySqlCommand cmd = new MySqlCommand())
                {
                    cmd.Connection = conn;
                    using (MySqlDataAdapter da = new MySqlDataAdapter (cmd))
                    {
                            da.InsertCommand = CreateInsertCommand(conn);
                            da.Update(myTable);
                    }
                }
            }
        }


C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
        static MySqlCommand CreateInsertCommand(MySqlConnection conn)
        {
            strSQl = @"INSERT INTO vorlagendetailalt (rechnungid, sprungmarke, detail, inhalt, vonSpalte, bisSpalte, extra, abstand, merge, 
                        aligment, border, schriftart, schriftgroesse) VALUES (?rechnungid, ?sprungmarke, ?detail, ?inhalt, ?vonSpalte, 
                        ?bisSpalte, ?extra, ?abstand, ?merge, ?aligment, ?border, ?schriftart, ?schriftgroesse)"
;

            MySqlCommand cmd = new MySqlCommand(strSQl, conn);
                cmd.Parameters.Add("?rechnungid", MySqlDbType.Int64 , 5 , "rechnungid");
                cmd.Parameters.Add("?sprungmarke", MySqlDbType.VarChar, 100,  "sprungmarke");
                cmd.Parameters.Add("?detail", MySqlDbType.VarChar, 50"detail");
                cmd.Parameters.Add("?inhalt", MySqlDbType.VarChar, 300"inhalt");
                cmd.Parameters.Add("?vonSpalte", MySqlDbType.Int16, 4"vonSpalte");
                cmd.Parameters.Add("?bisSpalte", MySqlDbType.Int16, 4"bisSpalte");
                cmd.Parameters.Add("?extra",MySqlDbType.VarChar, 50"exra");
                cmd.Parameters.Add("?abstand", MySqlDbType.Int16, 4"abstand");
                cmd.Parameters.Add("?merge", MySqlDbType.Int16, 4"merge");
                cmd.Parameters.Add("?aligment", MySqlDbType.VarChar, 50"aligment");
                cmd.Parameters.Add("?border", MySqlDbType.VarChar, 50"border");
                cmd.Parameters.Add("?schriftart", MySqlDbType.VarChar, 50"schriftart");
                cmd.Parameters.Add("?schriftgroesse", MySqlDbType.VarChar, 50"schriftgroesse");
            return cmd;
        }


Ralf Jansen - So 05.06.16 11:07

Sind denn überhaupt Daten in der DataTable die auch zurückgeschrieben werden müssen?

Wenn du aus Tabelle A liest um die nach Tabelle B zu schreiben wird das nicht so einfach funktionieren ohne Eingriff.
Baust du da gerade eine DataPump wo Quelle und Ziel unterschiedlich sind? Dann solltest du das näher erklären.

Für denn Fall das mein Näschen recht hat brauchst du das hier [ https://msdn.microsoft.com/de-de/library/system.data.datarow.setadded(v=vs.110).aspx].


Talemantros - So 05.06.16 12:17

Hallo Ralf,
in meinem Programm werden Dokumente wie Lieferscheine anhand von Parametern erzeugt die in der Tabelle  vorlagendetail liegen.
Wenn nun ein Original gedruckt wurde und irgendwann der Benutzer die Parameter für die Vorlage ändert würde das Duplikat nicht mehr aussehen wie das Original.
Daher wollte ich die Parameter mit dem ein Vordruck mal erzeugt wurde in eine extra Tabelle schreiben und immer wenn ein Duplikat erstellt wird darauf zurück greifen.

OriginalDaten : vorlagendetail
DuplikatDaten : vorlagendetailalt

Schaue mir mal deinen Link an.
Alternativ müsste ich halt um das DataTable eine ForEach machen und jede Zeile einzeln mit einer Klasse an die InsertMethode übergeben. Dachte aber das geht "schicker"


Ralf Jansen - So 05.06.16 12:28

Zitat:
Alternativ müsste ich halt um das DataTable eine ForEach machen und jede Zeile einzeln mit einer Klasse an die InsertMethode übergeben. Dachte aber das geht "schicker"

Wenn du alle Daten in deiner DataTable immer inserten willst egal welchen internen Zustand die haben dann rufe auf der DataTable zuerst AcceptChanges auf um die in einen eindeutigen unmodified state zu bringen und danach ruf für alle Rows SetAdded auf damit die als ~neu~ gelten. Dann sollten die Inserts vom DataAdapter auch ausgeführt werden.


C#-Quelltext
1:
2:
3:
myTable.AcceptChanges();
foreach (DataRow row in myTable.Rows)
   row.SetAdded();


Talemantros - So 05.06.16 12:35

Hi,
so wie du es beschrieben hast habe ich es gerade macht und wollte schreiben, dass es läuft.
Danke vielmals wie immer und schönen sonnigen Sonntag

Gruß