klasse persisten (mit ADO.NET)



  • hi,

    ich bin dabei einige programme von vb(6) auf c# zu portieren.
    schon dabei moechte ich natuerlich auch von ado 2.5 auf ado.net
    wechseln.

    nun meine frage. bei ado2.5 konnte man den recordset einfach updaten,
    bei ado.net funzt das updaten aber wohl nur ueber eine datenadapter,
    der mir aber in der klasse, nach dem fuellden des datasets nicht mehr
    zu verfuegung steht. gibt es eine update-methode mit ohne datenadapter??

    also code sieht ungefaehr so aus:
    vb 6:
    dim rs as adodb.recordset
    set rs = DataBase.openRs("SELECT ...")
    '...fuellmethode
    rs.update

    c#:
    DataSet ds = new DataSet();
    ds = DataBase.openDs("SELECT...")
    '...fuellmethode

    persist ???

    thx abs



  • DataAdapter selbst kann Daten weder lesen noch schreiben. Um Datensetze in einer DB zu manipulieren, nutzt DA vier verschiedene Sql- OleDbCommand's (SelectCommand, InsertCommand, UpdateCommand und DeleteCommand). Wenn du mal mit dem Assistenten so ein DA konfigurierst und dann in dem Sourcecode (Vom Windows Form-Designer generierter Code) nachschaust, wirst du sehen, was VS alles anlegt, damit es funzt. Du kannst dann so ein DA auch selbst zusammenstellen oder nur bestimmte xxxCommand's.



  • thx, war aber nicht, was ich gemeint hatte.
    ich haette aber auch etwas mehr code dabeischreiben koennen 🙄

    also wenn ich bei .net alles (lesen, aendern, schreiben)
    in einer methode mache sieht das ungefaehr so aus:

    string sql = "SELECT ... FROM ...";
    string conStr = "Provider=Microsoft.Jet.OLEDB.4.0;" + 
    		"Data Source=" + Application.StartupPath + "\\Content.mdb;" +				"Persist Security Info=False;";
    
    OleDbConnection conOle = new OleDbConnection(conStr);
    OleDbDataAdapter cmd = new OleDbDataAdapter(sql,conOle);				
    DataSet ds = new DataSet();
    
    cmd.Fill(ds); 
    DataRow dr = ds.Tables[0].Rows[0];    
    dr[0] = "hasenhirn";
    
    OleDbCommandBuilder cmdBuild = new OleDbCommandBuilder(cmd);
    cmd.Update(ds);
    

    wie gesagt, gab es beim activex ado eine update-methode beim recordset,
    die es ja jetzt nicht mehr gibt. man konnte z.b. also den recordset
    (allein) an eine function uebergeben und in dieser das update durchfuehren
    lassen.
    z.B
    ...
    UpdateRecordset rs
    ... >>>
    public punction UpdateRecordset(rs as adob.recordset) as boolean
    rs.update
    end function

    bei ado.net funzt das net mehr so (?)
    ...
    UpdateDataSet ds
    ... >>>
    public bool UpdateDataSet(DataSet ds)
    {
    //kein .update und hier keine dataadapter vorhanden
    }

    jetzt wollte ich halt wissen, ob's da ne andere moeglichkeit gibt
    als wie oben ueber den dataadapter zu updaten (bzw. explizit (nicht)
    ueber den dataadapter, der das dataset eingelesen hat).
    man kann sich zwar ne klasse dafuer schreiben, aber warum, wenn's
    einfacher geht 🕶



  • Hi,

    alternativ könntest du die geänderten Datensätze hohlen

    string sql = "SELECT ... FROM ..."; 
    string conStr = "Provider=Microsoft.Jet.OLEDB.4.0;" + 
    "Data Source=" + Application.StartupPath + "\\Content.mdb;" + "Persist Security Info=False;"; 
    
    OleDbConnection conOle = new OleDbConnection(conStr); 
    OleDbDataAdapter cmd = new OleDbDataAdapter(sql,conOle); 
    DataSet ds = new DataSet(); 
    
    cmd.Fill(ds); 
    DataRow dr = ds.Tables[0].Rows[0]; 
    dr[0] = "hasenhirn";
    DataSet Changes=ds.GetChanges(DataRowState.Modified); // zum Beispiel
    

    und selbst die Daten per SQL updaten.



  • stimmt, aber dann waere das fuellen und das upadten aber immmer noch in einer methode und nicht voneinander gekapselt.
    und das updaten per sql wollte ich ja gerade vermeiden (hab naemlich keinen bock fuer >100 klassen update-cmds zu schreiben...)

    ps:
    ich hab' aber gerade gelesen das in der naechsten dotnetpro was darueber steh'n soll



  • abbes,

    abbes schrieb:

    und das updaten per sql wollte ich ja gerade vermeiden (hab naemlich keinen bock fuer >100 klassen update-cmds zu schreiben...)

    ps:
    ich hab' aber gerade gelesen das in der naechsten dotnetpro was darueber steh'n soll

    da werden die Nachrichten für Dich vermutlich auch nicht viel besser werden. Falls doch, poste doch bitte etwas dazu. Ich sitze nämlich jeden Tag vor dem Rechner und hacke haufenweise SQL-Statements zusammen 🙄 ...

    Wenn Du das Erstellen der SQL-Statements vermeiden willst, hast Du imho zwei Möglichkeiten:
    1. Greife auf das Trio DataAdapter+CommandBuilder+DataSet zurück (siehe posting von Xqgene) und lasse den CommandBuilder die Erstellung von INSERT-, UPDATE- und DELETE-Statment erledigen. Das hat Nachteile und Einschränkungen, funktioniert aber.

    2. Verwende die "alten" (2.x) ADO-Bibliotheken (Import der COM Typ-Bibliotheken). Das hat natürlich auch wieder Nachteile. Vor allem soll wohl die Performance darunter leiden, da alle Daten von COM (Variant) zu den DotNet-Typen konvertiert werden müssen.



  • ich mache das jetzt (vorerst mal) so wie ich's oben geschrieben habe, also ueber den dataadapter (cmd.fill; werte-aendern; commanBuilder; cmd.update). funzt ja so und ich hatte damit bis jetzt noch keine probleme damit. die fuell- und update methoden hab' ich dann in 'ne 'business-object-base-class' gekapselt.

    das langt fuer die (unsere) business-klassen auf jeden fall (ich hab die uebrigens nochmal gezaehlt: sind 237, nix mit 100...)

    zu 2) stimmt, die werte muessen konvertiert werden. allerdings geht wohl das
    auslesen und schreiben ueber das active-x-ado wohl etwas schneller. kommt
    dann wahrscheinlich drauf an, wieviele werte du zu schreiben hast. aber
    dafuer dann extra 'en com-objekt einbinden: neeee, muss net. ausserdem
    gefaellt mir die getChanges-methode bei den datasets echt gut. macht
    wahrscheinlich auch noch einiges an zeit gut...

    mfg abs



  • abbes,

    abbes schrieb:

    ...ich hatte damit bis jetzt noch keine probleme damit. die fuell- und update methoden...

    dann möge es gelingen. Aber laß mich trotzdem die (mir bekannten) Fallstricke aufzählen, damit Du Dir später nicht vor Wut in's Knie beißt.

    1. Updates mit dem CommandBuilder funktionieren nur für jeweils eine Tabelle. SELECT-Statements, die über mehrere Tabellen gehen, werden also nicht unterstützt.

    2. Es ist stets ein eindeutiger Primär-Schlüssel erforderlich.

    3. Das automatisch generierte UPDATE-Statement enthält in der WHERE-Klausel Bedingungen, die zum Auswurf einer Exception führen, wenn der betreffende Datensatz während seiner Verweildauer in dem DataSet von einer anderen Verbindung aus verändert wurde - also etwa so:

    UPDATE Table
    SET Data=@Data
    WHERE (IDField = @Original_IDField) AND (Data = @Original_Data OR @Original_Data IS NULL AND Data IS NULL)
    

    Das ist ein "feature"!

    4. Felder mit Leerzeichen und sonstigen Nicht-Alphanumerischen Zeichen werden nicht unterstützt.



  • um Punkte 1..4 zu umgehen, kann man das alles auch "zu Fuss" machen. Hatte gerade so ein Fall. Daten werden aus zwei Tabellen gelesen und dann in zwei Tabellen wieder geschrieben:

    So wird mein DataAdapter konfiguriert und Daten eingelesen:

    private void LoadOrderLines()
    		{
    			try
    			{
    				// cmd_SelectCommand
    				cmd_SelectCommand = new SqlCommand();
    				cmd_SelectCommand.CommandText = @"SELECT SL.[Line No_], AL.[Original Quantity] AS 'Auftragsmenge', SL.No_ AS Artikel, SL.Description AS Beschreibung, SL.[Unit of Measure] AS Einheit, SL.[Qty_ to Ship] AS 'Gewogene Menge', AL.[Scales No_] AS 'Waagennr.', AL.[Gauge No_] AS 'Eichnr.' FROM [" + 
    					str_ClientName + "$Sales Line] SL INNER JOIN [" + str_ClientName + 
    				"$Additional Lines] AL ON SL.[Document Type] = AL.[Document Type] AND SL.[Document No_] = AL.[Document No_] AND SL.[Line No_] = AL.[Line No_] WHERE (SL.[Document Type] = 1) AND (SL.Type = 2) AND (SL.[Document No_] = @ORDERNO)";
    				cmd_SelectCommand.Connection = this.con_Connection;
    				cmd_SelectCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ORDERNO", System.Data.SqlDbType.VarChar, 20, "Document No_"));
    
    				da_SqlDataAdapter.SelectCommand = cmd_SelectCommand;
    
    				// cmd_UpdateCommand
    				cmd_UpdateCommand = new SqlCommand();
    				cmd_UpdateCommand.CommandText = 
    					"UPDATE dbo.[" + str_ClientName + "$Additional Lines] SET [Scales No_] = @SCALESNO, [Gauge No_] = @GAUGENO " +
    					"WHERE ([Document Type] = 1) AND ([Document No_] = @ORDERNO) AND ([Line No_] = @LINENO);" + 
    					"UPDATE dbo.[" + str_ClientName + "$Sales Line] SET [Quantity] = @QUANTITY1, [Qty_ to Ship] = @QUANTITY2 " +
    					"WHERE ([Document Type] = 1) AND ([Document No_] = @ORDERNO1) AND ([Line No_] = @LINENO1);";
    
    				this.cmd_UpdateCommand.Connection = this.con_Connection;
    
    				this.cmd_UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ORDERNO", System.Data.SqlDbType.VarChar, 20));
    				this.cmd_UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@LINENO", System.Data.SqlDbType.Int, 4, System.Data.ParameterDirection.Input, false, ((System.Byte)(0)), ((System.Byte)(0)), "Line No_", System.Data.DataRowVersion.Original, null));
    				this.cmd_UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@SCALESNO", System.Data.SqlDbType.VarChar, 30, "Waagennr."));
    				this.cmd_UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@GAUGENO", System.Data.SqlDbType.VarChar, 30, "Eichnr."));
    				this.cmd_UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@QUANTITY1", System.Data.SqlDbType.Decimal, 17, System.Data.ParameterDirection.Input, false, ((System.Byte)(38)), ((System.Byte)(20)), "Gewogene Menge", System.Data.DataRowVersion.Current, null));
    				this.cmd_UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@QUANTITY2", System.Data.SqlDbType.Decimal, 17, System.Data.ParameterDirection.Input, false, ((System.Byte)(38)), ((System.Byte)(20)), "Gewogene Menge", System.Data.DataRowVersion.Current, null));
    				this.cmd_UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@ORDERNO1", System.Data.SqlDbType.VarChar, 20));
    				this.cmd_UpdateCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("@LINENO1", System.Data.SqlDbType.Int, 4, System.Data.ParameterDirection.Input, false, ((System.Byte)(0)), ((System.Byte)(0)), "Line No_", System.Data.DataRowVersion.Original, null));
    
    				da_SqlDataAdapter.UpdateCommand  = cmd_UpdateCommand;
    
    				// Tabelle mit Daten füllen
    				da_SqlDataAdapter.SelectCommand.Parameters["@ORDERNO"].Value = str_OrderNo;
    				da_SqlDataAdapter.Fill(ds_OrderLines, "SL");
    
    				....
    			}
    			catch(Exception e)
    			{
    				EventLog.WriteLogEntry(e, this);
    				throw e; // Exception weiterleiten
    			}
    		}
    

    Daten zurück in die Datenbank schreiben:

    private void SaveChanges()
    		{
    			try
    			{
    				if (ds_OrderLines.HasChanges())
    				{
    					DataSet ds = ds_OrderLines.GetChanges();
    					da_SqlDataAdapter.UpdateCommand.Parameters["@ORDERNO"].Value = str_OrderNo;
    					da_SqlDataAdapter.UpdateCommand.Parameters["@ORDERNO1"].Value = str_OrderNo;
    					da_SqlDataAdapter.Update(ds, "SL");
    					con_Connection.Close();
    				}
    
    				....
    
    			}
    			catch(Exception ex)
    			{
    				EventLog.WriteLogEntry(ex, this);
    				MessageBox.Show(this, ex.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
    			}
    			finally
    			{
    				con_Connection.Close();
    			}
    
    			....
    		}
    


  • hey, danke fuer die viele hilfe leute 🙂

    werd's dann wohl bei dem cmd-builder bleiben, funzt bei mir soweit ganz gut (zumindest auf'm ms-sql-server-2000). fuer die richtig dicken brocken werde ich stored procedures anlegen, bzw. wo mehr als 30, 40 werte ge-updatet werden muessen.

    @dschensky:
    du solltest meine knier mal seh'n 😡

    zu 1: kann man nicht auch explizit ueber datatable updaten?
    muss das naechste woche nochmal nachschauen... (bin on tour)

    zu 4: musste ich leider auch schon lernen 😞

    @Xqgene:
    🙄 ganz dicker code, thx 👍


Anmelden zum Antworten