none
typisierte DataRow von DataSet für mehrere strukturgleiche Tabellen verwenden RRS feed

  • Frage

  • Hallo, ich hatte bsiher eine Tabelle mit Daten, in der ich je nach Eingabe einen einzigen Datensatz mit Daten für BErechnungen zurück bekomme. Nun muß ich die Tabelle clonen, weil die Daten die in der bisherigen Tabelle enthalten sind, noch andere Werte erhalten soll (aber die Struktur bleibt gleich).

    Nun möchte ich anhander der Eingabe vom User entweder den Datensatz aus der einen oder anderen Tabelle holen, und mit den Daten weiterarbeiten. Ich übergebe den Datensatz als typ. DataRow in weitere Klassen für Berechnungen.

    Wie löst man diese Problem am besten, ich brauche eine Idee damit ich in die korrekte Richtung laufe. Ich möchte ja nicht mehrfachen Code haben. Wieso kann man denn diese typ. DataRows nicht in die andere DataRow casten, wenn die doch die Tabellen identisch aufgebaut sind?

    Montag, 13. Dezember 2010 12:02

Antworten

  • Hallo Oema,

    hier ein Beispiel:

      // dt* hat einen Primärschlüssel ID [int/AutoIncrement] und Vorname, Nachame als string Spalte
      DataSet1.DataTable1DataTable dt1 = new DataSet1.DataTable1DataTable();
      DataSet1.DataTable2DataTable dt2 = new DataSet1.DataTable2DataTable();
    
      var dr1 = dt1.NewDataTable1Row();
      dr1.Vorname = "Vorname1"; dr1.Nachname = "Nachname1";
    
      var dr2 = dt2.NewDataTable2Row();
      dr2.ItemArray = dr1.ItemArray;
    

    _______________________

    btw: wenn Du eine neue DataRow erstellst, mache das normal über DataTable.NewRow-Methode oder eben wie oben durch die "NewDataTableRow" - Methoden der streng typisierten DataTable's, dadurch werden u.a. gleich die richtigen Primärschlüssel-Werte gesetzt.


    ciao Frank
    Dienstag, 14. Dezember 2010 06:33
  • Hallo,

    Du musst dabei schon die Werte übergeben.

    Ein Beispiel für die Northwind.Orders, weil die auch einen Autowert hat:

        private void CopyDataSetRow()
        {
          // Northwind.Orders abfüllen
          var ordersTable = new NorthwindDataSet.OrdersDataTable();
          var adapter = new OrdersTableAdapter();
          adapter.Fill(ordersTable);
    
          // Eine neue DataTable
          var ordersTableCopy = new NorthwindDataSet.OrdersDataTable();
    
          var foundRow = ordersTable.FirstOrDefault();
          if (foundRow != null)
          {
            // Übergeben der Werte an Add
            var newRow = ordersTableCopy.Rows.Add(foundRow.ItemArray) as NorthwindDataSet.OrdersRow;
    
            // ergibt gleiche IDs 
            Console.WriteLine("ID: {0} => {1}", foundRow.OrderID, newRow.OrderID);
          }
        }
    
    

    wobei ich via FirstOrDefault dein "Datensatzfinden" simuliert habe
    und dabei der Einfachheithalber die erste Zeile genommen habe.
    Der Cast auf eine typisierte OrdersRow ist erforderlich, wenn man typisiert
    weiterarbeiten will, da es keine typisierte RowsCollection gibt.

    Wenn man auf diese Weise eine neue Zeile einfügt und die AutoIncrement-Spalte übergibt,
    so erhält man in der kopierten Zeile den gleichen Wert wie im Original.
    Willst Du einen neuen Wert müsstest Du das ItemArray vorher verändern
    und für die ID Spalte Null / DBNull einsetzen.

    Gruß Elmar

    Dienstag, 14. Dezember 2010 10:17
    Beantworter

Alle Antworten

  • Hallo Omea,

          > Wieso kann man denn diese typ. DataRows nicht in die andere DataRow casten ...

    das ist die Typsicherheit in C#, die das (in der Form) nicht erlauben "darf", da es zwei verschiedene Typen sind (und keine implizite Typkonvertierung, oder entsprechende Vererbungs-Semantik definiert ist) - das ist schon richtig (sauber) so. Man kann aber normal in DataRow casten und dann das ItemArray-Eigenschaft auf das ItemArray der anderen Row setzen (am einfachsten).


    ciao Frank
    Montag, 13. Dezember 2010 13:21
  • Hallo,

    das geht bei der DataTable prinzipbedingt nicht.

    Die DataRow ist an die jeweilige DataTable gebunden,
    da die Inhalte selbst an der DataColumn hängen.
    Eine DataRow ist also kein unabhängiges Konstrukt -
    schon bei untypisierten DataSets nicht.

    Wenn Du die Daten in eine andere DataTable übertragen willst,
    musst Du eine Kopie der Daten erstellen, z. B. via ItemArray ,
    die Du der Add(object[]) Methode übergibst.
    (Identischer Aufbau inkl. Spalten-Reihenfolge vorausgesetzt).

    Gruß Elmar

    Montag, 13. Dezember 2010 13:33
    Beantworter
  • Hallo Elmar und Frank,

    ich habe es versucht, das umzusetzen. Aber ich bin nicht erfolgreich. Ich bekomme beim zuweisen zu der strong typed DataRow eine Fehlermeldung. Hier mal ein Codeausschnitt und die Fehlermeldung:

     

     drvTyp = DatensatzFinden("Rein_UmfEop");
                if (drvTyp.Row != null)
                {
                  DataRow[] rows = new DataRow[1];
                  rows[0] = drvTyp.Row;
    
                  Zeit_KalkulationDataSet.Rein_UmfDataTable umfTbl = new Zeit_KalkulationDataSet.Rein_UmfDataTable();
                  DataRowCollection drowCol = umfTbl.Rows;
    
                  Zeit_KalkulationDataSet.Rein_UmfRow umfRow = (Zeit_KalkulationDataSet.Rein_UmfRow)drowCol.Add((object)rows); // ==> bei dieser Zeile bekomme ich dann eine Fehlermeldung, dasss die Spalte ID nicht speichern kann (Cast-problem)
    .
    .
    .

     Fehlerausgabe:

    Unable to cast object of type 'System.Data.DataRow[]' to type 'System.IConvertible'.Couldn't store <System.Data.DataRow[]> in ID Column.  Expected type is Int32.

    Die ID hat AutoNumber und ist in beiden Tabellen auf long int gesetzt (Access-DB). Das versteh ich nicht, warum dieser Fehler gemeldet wird.

    Dienstag, 14. Dezember 2010 05:51
  • Hallo Oema,

    hier ein Beispiel:

      // dt* hat einen Primärschlüssel ID [int/AutoIncrement] und Vorname, Nachame als string Spalte
      DataSet1.DataTable1DataTable dt1 = new DataSet1.DataTable1DataTable();
      DataSet1.DataTable2DataTable dt2 = new DataSet1.DataTable2DataTable();
    
      var dr1 = dt1.NewDataTable1Row();
      dr1.Vorname = "Vorname1"; dr1.Nachname = "Nachname1";
    
      var dr2 = dt2.NewDataTable2Row();
      dr2.ItemArray = dr1.ItemArray;
    

    _______________________

    btw: wenn Du eine neue DataRow erstellst, mache das normal über DataTable.NewRow-Methode oder eben wie oben durch die "NewDataTableRow" - Methoden der streng typisierten DataTable's, dadurch werden u.a. gleich die richtigen Primärschlüssel-Werte gesetzt.


    ciao Frank
    Dienstag, 14. Dezember 2010 06:33
  • Hallo O.,

    > Unable to cast object of type 'System.Data.DataRow[]' to type 'System.IConvertible'.Couldn't store <System.Data.DataRow[]> in ID Column.  Expected type is Int32. Die ID hat AutoNumber und ist in beiden Tabellen auf long int gesetzt (Access-DB). Das versteh ich nicht, warum dieser Fehler gemeldet wird.

    Dein Code übergibt an drowCol.Add() ein DataRow[]-Array. Das verursacht eine ArgumentException, da der erwartete Parametertyp nicht mit dem übergebenen übereinstimmt: Erwartet wurde Int32, Du hast aber DataRow[] als Parameter geliefert. Auch wenn Du DataRow[] auf object castest wird daraus nicht das von Add() erwartete params object[] values. Der RuntimeType ist weiterhin DataRow[]. - Wie hier mehrfach erwähnt wurde, mußt Du an Add() das DataRow.ItemArray übergeben, quasi table1.Rows[0].ItemArray. Das führt dann dazu, dass in der zugrundeliegenden Tabelle eine neue Zeile hinzugefügt wird und die Werte der Ursprungszeile darin übernommen werden.

    Wenn ich mir das so ansehe, was Du bisher gezeigt hast, hat Deine Anwendung ein Desing-Problem. Der Flaschenhals ist ja nicht die Konvertibilität von typisierten oder untypisierten Zeilentypen, sondern dass Du diese Typen als Vehikel für den Datentransfer an eine Objekt-Methode benutzt, die eigtl. so wenig wie möglich über die konkrete implementation der Tabellenzeilen wissen sollte. Was passiert denn wenn Du eine dritte oder vierte Tabelle hinzufügen mußt? - Glücklicherweise gibt es im ADO.NET eine Klasse, die relativ inhaltsneutral ist und als Data Transfer Object (DTO) verwendet werden kann: das DataSet. Ich könnte mir gut vorstellen, dass Du die DatensatzFinden-Methode so umbauen könntest, dass sie ein DataSet als Parameter akzeptiert [dataSet.Tables.Add(dieJeweiligeTabelle(n))]. In der Methode selbst könntest Du LINQ to Dataset verwenden, um die gewünschten Operationen unabhängig von der konkreten Tabelle auszuführen. Sag mir Bescheid, wenn Du ein Beispiel brauchst.

    Implementing Data Transfer Object in .NET with a DataSet:
    http://msdn.microsoft.com/de-de/library/ff649325.aspx


    Gruß
    Marcel

     

    Dienstag, 14. Dezember 2010 08:09
    Moderator
  • Hallo,

    Du musst dabei schon die Werte übergeben.

    Ein Beispiel für die Northwind.Orders, weil die auch einen Autowert hat:

        private void CopyDataSetRow()
        {
          // Northwind.Orders abfüllen
          var ordersTable = new NorthwindDataSet.OrdersDataTable();
          var adapter = new OrdersTableAdapter();
          adapter.Fill(ordersTable);
    
          // Eine neue DataTable
          var ordersTableCopy = new NorthwindDataSet.OrdersDataTable();
    
          var foundRow = ordersTable.FirstOrDefault();
          if (foundRow != null)
          {
            // Übergeben der Werte an Add
            var newRow = ordersTableCopy.Rows.Add(foundRow.ItemArray) as NorthwindDataSet.OrdersRow;
    
            // ergibt gleiche IDs 
            Console.WriteLine("ID: {0} => {1}", foundRow.OrderID, newRow.OrderID);
          }
        }
    
    

    wobei ich via FirstOrDefault dein "Datensatzfinden" simuliert habe
    und dabei der Einfachheithalber die erste Zeile genommen habe.
    Der Cast auf eine typisierte OrdersRow ist erforderlich, wenn man typisiert
    weiterarbeiten will, da es keine typisierte RowsCollection gibt.

    Wenn man auf diese Weise eine neue Zeile einfügt und die AutoIncrement-Spalte übergibt,
    so erhält man in der kopierten Zeile den gleichen Wert wie im Original.
    Willst Du einen neuen Wert müsstest Du das ItemArray vorher verändern
    und für die ID Spalte Null / DBNull einsetzen.

    Gruß Elmar

    Dienstag, 14. Dezember 2010 10:17
    Beantworter
  • Hallo Omea,

    zur Vollständigkeit noch ... da Du von "DatensatzFinden" schreibst ...
    Du kannst, wenn Du Primärschlüsselfelder suchst, das auch über die eingebaute Methode Find der RowCollection machen:

    [Gewusst wie: Suchen einer bestimmten Zeile in einer DataTable]
    http://msdn.microsoft.com/de-de/library/y06xa2h1.aspx

    Die stark typisierten (autogenerierten) Methoden des DataSets wie FindBy**** können ggf. direkt benutzt werden. Aber sicher hast Du das schon in Deiner DatensatzFinden-Methode schon so implementiert.


    ciao Frank

    Dienstag, 14. Dezember 2010 12:06
  • Hallo Frank, genau das hat mir auch jemand gesagt, mit einer weiteren zugefügten methode für den TableAdapter, der dann aber die Daten aus der anderen strukturgleichen Tabelle liest. Das funktioniert auch prima.

    Aber eure Lösung gefällt mir besser, das funktioniert auch nun. Das war mein Problem das ich die Instanzen nicht von den typisierten DataSet-Objekten erstellt habe. Nun klappts, danke euch.

    Dienstag, 14. Dezember 2010 12:41