Abfragen in LINQ to DataSet

Aktualisiert: November 2007

Eine Abfrage ist ein Ausdruck, der Daten aus einer Datenquelle abruft. Abfragen werden in der Regel in einer speziellen Abfragesprache, wie SQL für relationale Datenbanken oder XQuery für XML, geschrieben. Aus diesem Grund mussten Entwickler bisher für jede neue Datenquelle oder jedes neue Datenformat eine neue Abfragesprache erlernen. Sprachintegrierte Abfrage (Language-Integrated Query, LINQ) stellt ein einfacheres, konsistentes Modell für die Arbeit mit Daten in verschiedenen Datenquellen und -formaten zur Verfügung. In einer LINQ-Abfrage arbeiten Sie immer mit Programmierobjekten.

Eine LINQ-Abfrageoperation besteht aus drei Aktionen: Abrufen der Datenquelle(n), Erstellen der Abfrage und Ausführen der Abfrage.

Datenquellen, die die generische IEnumerable<T>-Schnittstelle implementieren, können über LINQ abgefragt werden. Beim Aufrufen von AsEnumerable für eine DataTable wird ein Objekt zurückgegeben, das die generische IEnumerable<T>-Schnittstelle implementiert, die als Datenquelle für LINQ to DataSet-Abfragen dient.

In der Abfrage geben Sie genau die Informationen an, die aus der Datenquelle abgerufen werden sollen. In der Abfrage kann auch angegeben werden, wie die Abfrageergebnisse sortiert, gruppiert und formatiert werden sollen, bevor sie zurückgegeben werden. In LINQ wird eine Abfrage in einer Variablen gespeichert. Wenn die Abfrage so eingerichtet ist, dass sie eine Sequenz von Werten zurückgibt, muss die Abfragevariable selbst ein aufzählbarer Typ sein. Diese Abfragevariable führt keine Aktion aus und gibt keine Daten zurück; sie dient lediglich zur Speicherung der Abfrageinformationen. Nachdem Sie eine Abfrage erstellt haben, müssen Sie sie ausführen, damit Daten abgerufen werden.

In einer Abfrage, die eine Sequenz von Werten zurückgibt, enthält die Abfragevariable selbst niemals die Abfrageergebnisse, sondern immer nur die Abfragebefehle. Die Ausführung der Abfrage wird verzögert, bis die Abfragevariable in einer foreach- oder For Each-Schleife durchlaufen wird. Dies wird als verzögerte Ausführung bezeichnet, das heißt, die Abfrage wird zu einem späteren Zeitpunkt ausgeführt und nicht sofort bei ihrer Erstellung. Auf diese Weise können Sie die Abfrage so oft ausführen, wie Sie dies wünschen. Dies bietet sich z. B. dann an, wenn Sie eine Datenbank haben, die von anderen Anwendungen aktualisiert wird. Sie können in Ihrer Anwendung eine Abfrage erstellen, mit der die neuesten Informationen abgerufen werden, und diese Abfrage wiederholt ausführen, wobei jedes Mal die aktualisierten Informationen zurückgegeben werden.

Im Gegensatz zu zurückgestellten Abfragen, die eine Sequenz von Werten zurückgeben, werden Abfragen, die nur einen einzigen Wert zurückgeben (SINGLETON-Abfragen), sofort ausgeführt. Einige Beispiele für SINGLETON-Abfragen sind Count, Max, Average und First. Die Ausführung dieser Abfragen erfolgt sofort, weil die Abfrageergebnisse für die Berechnung des SINGLETON-Ergebnisses benötigt werden. Um z. B. den Durchschnittswert der Abfrageergebnisse zu ermitteln, muss die Abfrage so ausgeführt werden, dass die Durchschnittsberechnungsfunktion über Eingabedaten verfügt, mit denen sie arbeiten kann. Zur Erzwingung der sofortigen Ausführung einer Abfrage, die keinen SINGLETON-Wert zurückgibt, können Sie die ToList<TSource>-Methode oder die ToArray<TSource>-Methode verwenden. Dieses Verfahren zur Erzwingung der sofortigen Abfrageausführung kann sinnvoll sein, wenn Sie die Ergebnisse einer Abfrage zwischenspeichern möchten. Weitere Informationen über die verzögerte und sofortige Abfrageausführung finden Sie unter Erste Schritte mit LINQ.

Abfragen

LINQ to DataSet-Abfragen können in zwei verschiedenen Syntaxarten formuliert werden: in der Abfrageausdrucksyntax und in der methodenbasierten Abfragesyntax.

Abfrageausdruckssyntax

Abfrageausdrücke sind eine deklarative Abfragesyntax. Mit dieser Syntax kann der Entwickler Abfragen in einem SQL-ähnlichen Format in C# oder Visual Basic schreiben. Die Abfrageausdruckssyntax ermöglicht die Ausführung komplexer Filter-, Sortier- und Gruppiervorgänge mit minimalem Codeeinsatz. Weitere Informationen finden Sie unter LINQ-Abfrageausdrücke (C#-Programmierhandbuch) und Grundlegende Abfrageoperationen (Visual Basic).

Die Abfrageausdruckssyntax ist ein in C# 3.0 und Visual Basic 2008 neu eingeführtes Feature. Beachten Sie jedoch, dass die .NET Framework-Common Language Runtime (CLR) die Abfrageausdruckssyntax selbst nicht lesen kann. Daher werden die Abfrageausdrücke beim Kompilieren in etwas übersetzt, was die CLR versteht: Methodenaufrufe. Diese Methoden werden als Standardabfrageoperatoren bezeichnet. Als Entwickler können Sie entscheiden, ob Sie die Methoden mittels Methodensyntax direkt aufrufen möchten oder ob dafür die Abfragesyntax verwendet werden soll. Weitere Informationen dazu finden Sie unter Abfragesyntax und Methodensyntax (LINQ). Weitere Informationen zur Verwendung der Standardabfrageoperatoren finden Sie unter Allgemeines LINQ-Programmierhandbuch.

In den folgenden Beispielen wird die Select-Methode verwendet, um alle Zeilen aus der Product-Tabelle zurückzugeben und die Produktnamen anzuzeigen.

' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = From product In products.AsEnumerable() _
            Select product
Console.WriteLine("Product Names:")
For Each p In query
    Console.WriteLine(p.Field(Of String)("Name"))
Next
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> query =
    from product in products.AsEnumerable()
    select product;

Console.WriteLine("Product Names:");
foreach (DataRow p in query)
{
    Console.WriteLine(p.Field<string>("Name"));
}

Methodenbasierte Abfragesyntax

Statt mit der Abfrageausdruckssyntax können LINQ to DataSet-Abfragen auch mit der methodenbasierten Abfragesyntax formuliert werden. Dabei handelt es sich um eine Abfolge direkter Methodenaufrufe der LINQ-Operatormethoden, wobei als Parameter Lambda-Ausdrücke übergeben werden. Weitere Informationen dazu finden Sie unter Lambda-Ausdrücke (C#-Programmierhandbuch).

In diesem Beispiel wird Select verwendet, um alle Zeilen aus der Product-Tabelle zurückzugeben und die Produktnamen anzuzeigen.

' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = products.AsEnumerable() _
    .Select(Function(product As DataRow) New With _
    { _
        .ProductName = product.Field(Of String)("Name"), _
        .ProductNumber = product.Field(Of String)("ProductNumber"), _
        .Price = product.Field(Of Decimal)("ListPrice") _
    })

Console.WriteLine("Product Info:")
For Each product In query
    Console.Write("Product name: " & product.ProductName)
    Console.Write("Product number: " & product.ProductNumber)
    Console.WriteLine("List price: $ " & product.Price)
Next
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

var query = products.AsEnumerable().
    Select(product => new
    {
        ProductName = product.Field<string>("Name"),
        ProductNumber = product.Field<string>("ProductNumber"),
        Price = product.Field<decimal>("ListPrice")
    });

Console.WriteLine("Product Info:");
foreach (var productInfo in query)
{
    Console.WriteLine("Product name: {0} Product number: {1} List price: ${2} ",
        productInfo.ProductName, productInfo.ProductNumber, productInfo.Price);
}

Verfassen von Abfragen

Wie weiter oben bereits erwähnt, speichert die Abfragevariable selbst nur die Abfragebefehle, sofern die Abfrage eine Sequenz von Werten zurückgeben soll. Wenn die Abfrage keine Methode enthält, die eine sofortige Ausführung erzwingt, wird die Ausführung der Abfrage so lange verzögert, bis die Abfragevariable in einer foreach- oder For Each-Schleife durchlaufen wird. Die verzögerte Ausführung ermöglicht die Kombination mehrerer Abfragen oder die Erweiterung einer bestehenden Abfrage durch das Hinzufügen neuer Operationen. Die Änderungen werden dann bei der Ausführung der Abfrage berücksichtigt. Im folgenden Beispiel gibt die erste Abfrage alle Produkte zurück. Die zweite Abfrage erweitert die erste, indem sie Where verwendet, um alle Produkte der Größe "L" zurückzugeben:

' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim productsQuery = From product In products.AsEnumerable() _
            Select product

Dim largeProducts = _
    productsQuery.Where(Function(p) p.Field(Of String)("Size") = "L")

Console.WriteLine("Products of size 'L':")
For Each product In largeProducts
    Console.WriteLine(product.Field(Of String)("Name"))
Next

// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> productsQuery =
    from product in products.AsEnumerable()
    select product;

IEnumerable<DataRow> largeProducts =
    productsQuery.Where(p => p.Field<string>("Size") == "L");

Console.WriteLine("Products of size 'L':");
foreach (DataRow product in largeProducts)
{
    Console.WriteLine(product.Field<string>("Name"));
}


Nach Ausführung der Abfrage können keine weiteren Abfragen mehr verfasst werden. Alle nachfolgenden Abfragen verwenden die im Arbeitsspeicher abgelegten LINQ-Operatoren. Die Abfrage wird ausgeführt, sobald die Abfragevariable in einer foreach- oder For Each-Anweisung durchlaufen wird oder wenn einer der LINQ-Konvertierungsoperatoren aufgerufen wird, die eine sofortige Ausführung erzwingen. Dabei kann es sich um einen der folgenden Operatoren handeln: ToList<TSource>, ToArray<TSource>, ToLookup und ToDictionary.

Im folgenden Beispiel gibt die erste Abfrage alle Produkte zurück und sortiert sie nach dem Listenpreis. Um die sofortige Abfrageausführung zu erzwingen, wird die ToArray<TSource>-Methode verwendet:

' Fill the DataSet.
Dim ds As New DataSet()
ds.Locale = CultureInfo.InvariantCulture
' See the FillDataSet method in the Loading Data Into a DataSet topic.
FillDataSet(ds)

Dim products As DataTable = ds.Tables("Product")

Dim query = _
        From product In products.AsEnumerable() _
        Order By product.Field(Of Decimal)("ListPrice") Descending _
        Select product

' Force immediate execution of the query.
Dim productsArray = query.ToArray()

Console.WriteLine("Every price From highest to lowest:")
For Each prod In productsArray
    Console.WriteLine(prod.Field(Of Decimal)("ListPrice"))
Next
// Fill the DataSet.
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
FillDataSet(ds);

DataTable products = ds.Tables["Product"];

IEnumerable<DataRow> query =
    from product in products.AsEnumerable()
    orderby product.Field<Decimal>("ListPrice") descending
    select product;

// Force immediate execution of the query.
IEnumerable<DataRow> productsArray = query.ToArray();

Console.WriteLine("Every price from highest to lowest:");
foreach (DataRow prod in productsArray)
{
    Console.WriteLine(prod.Field<Decimal>("ListPrice"));
}

Siehe auch

Konzepte

Abfragen von 'DataSets' (LINQ to DataSet)

Weitere Ressourcen

Informationen zum Programmieren (LINQ to DataSet)

Erste Schritte mit LINQ in C#

Erste Schritte mit LINQ in Visual Basic