## 26.6 Objekte vom Typ »DataView« Wenn Sie die Select-Methode der DataTable benutzen, sollten Sie sich über zwei Nachteile im Klaren sein: Sie arbeitet nicht effizient mit den Daten. Der Rückgabewert ist immer ein DataRow-Array, das von WinForms und Webformularen nicht unterstützt wird. Diese Nachteile hat ein DataView-Objekt nicht. DataViews repräsentieren eine einfache Möglichkeit, verschiedene Sichten auf einen Datenbestand anzubieten – ähnlich den Views einer Datenbank. Ausgangsbasis für eine DataView ist jedoch kein SQL, sondern eine DataTable. So könnte man zum Beispiel eine DataTable mit den Bestellungen eines Kunden füllen und eine DataView erzeugen, die nur die Bestellungen anzeigt, die noch offen sind. Eine weitere DataView könnte gleichzeitig alle Bestellungen darstellen, die schon abgeschlossen sind. Im Gegensatz zur Select-Methode können mehrere Sichten gleichzeitig angezeigt werden, ohne eine Kopie der Daten erstellen zu müssen. Außerdem werden DataView-Objekte automatisch aktualisiert, sobald sich die Daten in der DataTable ändern. Zudem bietet eine DataView eine bessere Unterstützung für das Filtern von Daten als das DataTable-Objekt. Ein DataView-Objekt verwaltet keine eigene Kopie der Daten. Stattdessen greift eine DataView auf Daten zurück, die in einer DataTable gespeichert sind. Daten, die in zwei verschiedenen Tabellen gespeichert sind, lassen sich mit einer DataView nicht verknüpfen. Mit anderen Worten: Eine DataView kann nur auf eine DataTable zugreifen. ### 26.6.1 »DataView« erzeugen Die Klasse DataView definiert drei Konstruktoren. Der einfachste ist der parameterlose. Wenn Sie diesen benutzen, müssen Sie in einer weiteren Anweisung das DataView-Objekt mit einer DataTable verknüpfen. Dazu dient die Eigenschaft Table.
    DataView view = new DataView();
    view.Table = ds.Tables[0];
Der einfach parametrisierte Konstruktor nimmt direkt die Referenz auf die DataTable entgegen.
    DataView view = new DataView(ds.Tables[0]);
Der dritte Konstruktor erinnert an die Select-Methode der Klasse DataTable. Im ersten Parameter erwartet er die Referenz auf die DataTable, im zweiten wird ein Filterkriterium angegeben, im dritten das Sortierkriterium und schließlich im vierten ein Wert vom Typ DataViewRowState.
    DataView view = new DataView(tbl, "", "", DataViewRowState.Unchanged);
Der letztgenannte Konstruktor weist den Eigenschaften Table, RowFilter, Sort und RowStateFilter der DataView sofort die entsprechenden Werte zu. ### 26.6.2 Auf die Datenzeilen in einer »DataView« zugreifen Während Sie über die Eigenschaft Rows einer DataTable an die Auflistung aller Datenzeilen einer DataTable gelangen, verhält sich eine DataView selbst wie eine Collection. Sie können sie daher in einer foreach-Schleife durchlaufen. Die zurückgelieferte Datenzeile ist vom Typ DataRowView. Die DataRowView werten Sie aus, indem Sie dem Indexer den Bezeichner der Spalte oder dessen Index übergeben.
    DataView view = new DataView(ds.Tables["Artikel"]);
    foreach (DataRowView rowView in view)
      Console.WriteLine(rowView["ProductName"]);
Da sich eine DataView wie eine Auflistung verhält, verwundert es nicht, dass die Eigenschaft Count die Anzahl der DataRowView-Objekte zurückliefert. Damit haben Sie die Möglichkeit, eine DataView auch in einer for-Schleife zu durchlaufen. ### 26.6.3 Die Eigenschaft »Sort« und die Methode »Find« Die Find-Methode einer DataView dient dazu, eine ganz bestimmte Datenzeile zu suchen. Allerdings ist daran eine Bedingung geknüpft: der Sort-Methode muss zuvor ein gültiger Spaltenbezeichner übergeben werden. Sort beschreibt immer nur einen Spaltennamen, optional gefolgt von ASC (aufsteigend) oder DESC (absteigend). Der Find-Methode wird dann der Wert übergeben, nach dem in der unter Sort angegebenen Spalte gesucht wird. Ungewöhnlich ist der Rückgabewert der Find-Methode. Es ist ein Integer, der den Index der gefundenen Datenzeile in der DataView angibt. Wird keine Datenzeile gefunden, ist der Wert –1.
    DataView view = new DataView(ds.Tables[0]);
    view.Sort = "ProductName";
    int index = view.Find("Chai");
    if (index != -1)
      Console.WriteLine("Artikel: {0}", view[index]["UnitPrice"]);
    else
      Console.WriteLine("Keine Datenzeile gefunden.");
### 26.6.4 Die Methode »FindRows« Die Find-Methode einer DataRowCollection und die Find-Methode einer DataView ähneln sich in gewisser Hinsicht, denn beide liefern nur eine Datenzeile zurück. Die Find-Methode der DataRowCollection tut dies, weil diese Find-Methode die Angabe des Primärschlüssels des zu suchenden Datensatzes erwartet, und die Find-Methode der DataView verhält sich so, weil sie per Definition nur einen Integer-Wert liefert. Wenn Sie aber zum Beispiel eine Sicht auf die DataTable der Lieferanten erstellen und der Sort-Eigenschaft eine Stadt übergeben, könnte es sein, dass mehrere Lieferanten in der angegebenen Stadt sesshaft sind. In diesem Fall ist die Find-Methode denkbar ungeeignet. Eine DataView stellt dafür die Methode FindRows bereit. Im Unterschied zu Find ist der Rückgabewert ein DataRowView-Array.
    DataView view = new DataView(ds.Tables[0]);
    view.Sort = "CategoryID";
    DataRowView[] rowArr = view.FindRows(1);
    foreach (DataRowView row in rowArr)
      Console.WriteLine("Artikel: {0}", row["ProductName"]);
Das Codefragment beantwortet die Frage, welche Artikel der Tabelle Products alle der Kategorie-Nummer 1 zugeordnet werden. ### 26.6.5 Die Eigenschaft »RowFilter« Die Eigenschaft RowFilter dient zum Selektieren von Datenzeilen. Sie ist vom Typ einer Zeichenfolge und unterscheidet sich nicht vom Filterausdruck der Select-Methode der DataTable. Sie geben also das Filterkriterium an, als würden Sie in einem SQL-Statement eine WHERE-Klausel definieren. Nur die Angabe von WHERE ist nicht notwendig.
    DataView view = new DataView(ds.Tables[0]);
    view.RowFilter = "ProductName LIKE 'C*'";
    foreach (DataRowView rowView in view)
      Console.WriteLine(rowView["ProductName"]);
### 26.6.6 Die Eigenschaft »RowStateFilter« Die Eigenschaft RowStateFilter akzeptiert Werte der Enumeration DataViewRowState (siehe Tabelle 26.6). Während bei der Ausgabe einer DataTable unabhängig davon, ob bei der Filterung ModifiedCurrent und ModifiedOriginal benutzt wurden, kein Unterschied festzustellen war, werden bei einer DataView tatsächlich entweder die aktuellen oder die ursprünglichen Werte in der Ausgabe erscheinen.
    DataView view = new DataView(ds.Tables[0]);
    view.RowStateFilter = DataViewRowState.Added |
                        DataViewRowState.Deleted;
    foreach (DataRowView rowView in view)
      Console.WriteLine(rowView["ProductName"]);
### 26.6.7 Änderungen an einem »DataView«-Objekt Eine DataView ist nicht statisch. Sie können zusätzliche DataRowView-Objekte hinzufügen, oder Sie können eine DataRowView löschen oder deren Inhalt ändern. Um eine DataRowView hinzuzufügen, stellt die DataView die Methode AddNew bereit. Diese gibt ein neues DataRowView-Objekt zurück, dessen Spalten mit Daten gefüllt werden können. Zum Schluss muss auf der DataRowView die Methode EndEdit aufgerufen werden. Die Änderung ähnelt der einer DataRow. Mit BeginEdit wird die Änderung eingeleitet, und mit EndEdit wird sie abgeschlossen. Ein Abbruch kann mit CancelEdit erzwungen werden. Alles sind Methoden des DataRowView-Objekts. Um eine DataRowView zu löschen, brauchen Sie nur die Methode Delete aufzurufen. Das folgende Beispielprogramm zeigt Ihnen alle zuvor beschriebenen Änderungsmöglichkeiten. Ausgegeben werden sollen am Ende des Programms nur die geänderten DataRowViews. Dazu wird der Eigenschaft RowStateFilter eine passende Kombination aus den erforderlichen DataRowViewState-Konstanten übergeben.
    // ---------------------------------------------------
    // Beispiel: ...\Kapitel 26\EditDataView
    // ---------------------------------------------------
    class Program {
      static void Main(string[] args) {
        SqlConnection con = new SqlConnection();
        con.ConnectionString = "...";
        SqlCommand cmd = new SqlCommand();
        cmd.Connection = con;
        cmd.CommandText = "SELECT ProductName, UnitPrice FROM Products";
        DataSet ds = new DataSet();
        SqlDataAdapter da = new SqlDataAdapter(cmd);
        da.Fill(ds);
        // DataView erzeugen
        DataView dv = new DataView(ds.Tables[0]);
        // DataRowView hinzufügen
        DataRowView newRow = dv.AddNew();
        newRow["ProductName"] = "Schokolade";
        newRow["UnitPrice"] = 15.99;
        newRow.EndEdit();
        // DataRowView ändern
        dv[0].BeginEdit();
        dv[0]["ProductName"] = "Eisbein";
        dv[0].EndEdit();
        // DataRowView löschen
        dv[1].Delete();
        dv.RowStateFilter = DataViewRowState.Added |
                            DataViewRowState.Deleted |
                            DataViewRowState.ModifiedOriginal;
        // Ausgabe der DataView
        foreach (DataRowView rowView in dv)
          Console.WriteLine(rowView["ProductName"]);
        Console.ReadLine();
      }
    }
### 26.6.8 Aus einer »DataView« eine »DataTable« erzeugen Ihnen liegt eine DataView vor, und Sie möchten diese nun als DataTable speichern? Kein Problem, denn mit der Methode ToTable werden alle Datenzeilen einer DataTable zugeführt, die über die Einstellung der Eigenschaft RowFilter verfügbar sind. Da ToTable überladen ist, haben Sie mehrere Alternativen, diesen Vorgang zu steuern. So können Sie den Namen der DataTable schon beim Methodenaufruf festlegen und die Spalten festlegen, die der DataTable übergeben werden sollen. Ein weiterer Parameter gestattet es Ihnen, zu spezifizieren, ob die resultierende DataTable nur eindeutige Zeilen basierend auf den angegebenen DataColumns erhält. Ein Beispiel mit ToTable soll dieses Kapitel abschließen. Zuerst werden die Spalten ProductName, UnitPrice und UnitsInStock der Tabelle Products in eine DataTable geladen. Eine DataView beschränkt die Sicht auf einen Teilbereich dieser DataTable und enthält nur die Datenzeilen der Artikel, die mit dem Buchstaben »C« beginnen und deren Einzelpreis kleiner 30 ist. Mit
    DataTable tbl = dv.ToTable("C_Products", false,
                    new string[]{"UnitPrice", "ProductName" });
wird die DataView danach in eine DataTable geschrieben, deren Bezeichner auf C_Products festgelegt ist. Die neue DataTable enthält aber nur die Spalten UnitPrice und ProductName. Der boolesche Parameter gibt Auskunft darüber, ob alle Zeilen eindeutig sein sollen. Er ist hier auf false festgelegt, sodass durchaus auch zwei inhaltsgleiche Zeilen in der resultierenden DataTable erscheinen könnten.
    // ---------------------------------------------------
    // Beispiel: ...\Kapitel 26\ToTableMethod
    // ---------------------------------------------------
    class Program {
      static void Main(string[] args){
        SqlConnection con = new SqlConnection();
        con.ConnectionString = "...";
        SqlCommand cmd = new SqlCommand();
        cmd.Connection = con;
        cmd.CommandText = "SELECT ProductName, UnitPrice, UnitsInStock"’
                          +" FROM Products";
        DataSet ds = new DataSet();
        SqlDataAdapter da = new SqlDataAdapter(cmd);
        da.Fill(ds);
        // DataView erzeugen
        DataView dv = new DataView(ds.Tables[0]);
        dv.RowFilter = "ProductName LIKE 'C*' AND UnitPrice < 30";
        // DataView einer DataTable übergeben
        DataTable tbl = dv.ToTable("C_Products", false,
               new string[] { "UnitPrice", "ProductName" });
        foreach (DataRow row in tbl.Rows)
          Console.WriteLine("{0,-10}{1}", row[0], row[1]);
        Console.ReadLine();
      }
    }