Schreiben der ersten LINQ-Abfrage (Visual Basic)

Eine Abfrage ist ein Ausdruck, der Daten von einer Datenquelle abruft. Abfragen werden in einer dedizierten Abfragesprache ausgedrückt. Im Laufe der Zeit wurden verschiedene Sprachen für die verschiedenen Datenquellen entwickelt, beispielsweise SQL für relationale Datenbanken und XQuery für XML. Dies macht es erforderlich, dass Anwendungsentwickler*innen eine neue Abfragesprache für jeden unterstützten Datenquellen- oder Datenformattyp erlernen müssen.

LINQ (Language Integrated Query) vereinfacht diese Situation durch die Bereitstellung eines konsistenten Modells zum Arbeiten mit Daten in verschiedenen Arten von Datenquellen und Formaten. In einer LINQ-Abfrage arbeiten Sie immer mit Objekten. Sie verwenden dieselben grundlegenden Codierungsmuster für die Abfrage und Transformation von Daten in XML-Dokumenten, SQL-Datenbank-Instanzen, ADO.NET-Datasets und -Entitäten, .NET Framework-Sammlungen sowie allen anderen Quellen oder Formaten, für die ein LINQ-Anbieter verfügbar ist. In diesem Dokument werden die drei Phasen der Erstellung und Verwendung grundlegender LINQ-Abfragen beschrieben.

Drei Phasen eines Abfragevorgangs

Alle LINQ-Abfragevorgänge bestehen aus drei Aktionen:

  1. Abrufen der Datenquellen

  2. Erstellen der Abfrage

  3. Ausführen der Abfrage

In LINQ unterscheidet sich die Ausführung einer Abfrage von der Erstellung der Abfrage. Sie rufen keine Daten ab, indem Sie nur eine Abfrage erstellen. Dieser Punkt wird in diesem Thema an späterer Stelle ausführlicher behandelt.

Das folgende Beispiel veranschaulicht die drei Teile eines Abfragevorgangs. Im Beispiel wird ein Array aus ganzen Zahlen als praktische Datenquelle zu Demonstrationszwecken verwendet. Dieselben Konzepte gelten jedoch auch für andere Datenquellen.

Hinweis

Stellen Sie auf der Seite „Kompilieren“ im Projekt-Designer (Visual Basic) sicher, dass Option infer auf Ein festgelegt ist.

' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}

' Query creation.
Dim evensQuery = From num In numbers
                 Where num Mod 2 = 0
                 Select num

' Query execution.
For Each number In evensQuery
    Console.Write(number & " ")
Next

Ausgabe:

0 2 4 6

Die Datenquelle

Da es sich bei der Datenquelle im vorherigen Beispiel um ein Array handelt, unterstützt sie implizit die generische IEnumerable<T>-Schnittstelle. Diese Tatsache ermöglicht es Ihnen, ein Array als Datenquelle für eine LINQ-Abfrage zu verwenden. Typen, die IEnumerable<T> unterstützen oder eine abgeleitete Schnittstelle, wie z.B. der generische Typ IQueryable<T>, werden als abfragbare Typen bezeichnet.

Da es sich um einen impliziten abfragbaren Typ handelt, erfordert das Array keine Änderung oder besondere Behandlung, um es als LINQ-Datenquelle zu verwenden. Dasselbe gilt für jeden Sammlungstyp, der IEnumerable<T> unterstützt, einschließlich der generischen Klassen List<T> und Dictionary<TKey,TValue> und anderer Klassen in der .NET Framework-Klassenbibliothek.

Wenn die Quelldaten IEnumerable<T> noch nicht implementieren, ist ein LINQ-Anbieter erforderlich, um die Funktionalität der Standardabfrageoperatoren für diese Datenquelle zu implementieren. Beispielsweise übernimmt LINQ to XML das Laden eines XML-Dokuments in einen abfragbaren XElement-Typ, wie im folgenden Beispiel gezeigt. Weitere Informationen zu Standardabfrageoperatoren finden Sie unter Standard Query Operators Overview (Visual Basic) (Übersicht über Standardabfrageoperatoren (Visual Basic)).

' Create a data source from an XML document.
Dim contacts = XElement.Load("c:\myContactList.xml")

Mit LINQ to SQL erstellen Sie zuerst eine objektrelationale Zuordnung zur Entwurfszeit, entweder manuell oder mit den LING in SQL-Tools in Visual Studio. Sie schreiben die Abfragen für die Objekte, und zur Laufzeit übernimmt LINQ to SQL die Kommunikation mit der Datenbank. Im folgenden Beispiel stellt customers eine bestimmte Tabelle in der Datenbank dar, und Table<TEntity> unterstützt die generische Schnittstelle IQueryable<T>.

' Create a data source from a SQL table.
Dim db As New DataContext("C:\Northwind\Northwnd.mdf")
Dim customers As Table(Of Customer) = db.GetTable(Of Customer)

Weitere Informationen zum Erstellen bestimmter Typen von Datenquellen finden Sie in der Dokumentation der verschiedenen LINQ-Anbieter. (Eine Liste dieser Anbieter finden Sie unter Language Integrated Query (LINQ) – Visual Basic.) Die Grundregel ist einfach: Eine LINQ-Datenquelle ist jedes Objekt, das die generische IEnumerable<T>-Schnittstelle oder eine Schnittstelle unterstützt, die davon erbt.

Hinweis

Typen wie ArrayList, die die nicht generische IEnumerable-Schnittstelle unterstützen, können ebenso als LINQ-Datenquellen verwendet werden. Ein Beispiel, das eine Arrayliste (ArrayList) verwendet, finden Sie unter Abfragen einer ArrayList mit LINQ (Visual Basic).

Die Abfrage

In der Abfrage geben Sie an, welche Informationen aus den Datenquellen abgerufen werden sollen. Sie können auch angeben, wie diese Informationen sortiert, gruppiert oder strukturiert werden sollen, bevor sie zurückgegeben werden. Zum Aktivieren der Abfrageerstellung hat Visual Basic eine neue Abfragesyntax in die Sprache integriert.

Bei der Ausführung gibt die Abfrage im folgenden Beispiel alle geraden Zahlen aus dem Ganzzahlarray numbers zurück:

' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}

' Query creation.
Dim evensQuery = From num In numbers
                 Where num Mod 2 = 0
                 Select num

' Query execution.
For Each number In evensQuery
    Console.Write(number & " ")
Next

Der Abfrageausdruck enthält drei Klauseln: From, Where und Select. Die jeweilige Funktion und der Zweck der einzelnen Abfrageausdrucksklauseln werden unter Grundlegende Abfragevorgänge (Visual Basic) erläutert. Weitere Informationen finden Sie unter Abfragen. Beachten Sie, dass in LINQ eine Abfragedefinition häufig in einer Variablen gespeichert und später ausgeführt wird. Die Abfragevariable (etwa evensQuery im vorherigen Beispiel) muss ein abfragbarer Typ sein. Der Typ von evensQuery ist IEnumerable(Of Integer) und wird vom Compiler mithilfe eines lokalen Typrückschlusses zugewiesen.

Denken Sie unbedingt daran, dass die Abfragevariable selbst keine Aktion ausführt und keine Daten zurückgibt. Sie speichert nur die Abfragedefinition. Im vorherigen Beispiel führt die For Each-Schleife die Abfrage aus.

Abfrageausführung

Die Abfrageausführung ist getrennt von der Abfrageerstellung. Die Abfrageerstellung definiert die Abfrage, die Ausführung wird jedoch von einem anderen Mechanismus ausgelöst. Eine Abfrage kann ausgeführt werden, sobald sie definiert ist (unmittelbare Ausführung), oder die Definition kann gespeichert und die Abfrage kann später ausgeführt werden (verzögerte Ausführung).

Verzögerte Ausführung

Eine typische LINQ-Abfrage ähnelt der im vorherigen Beispiel, in der evensQuery definiert ist. Die Abfrage wird erstellt, aber nicht sofort ausgeführt. Stattdessen wird die Abfragedefinition in der Abfragevariablen evensQuery gespeichert. Sie führen die Abfrage später aus. Dazu verwenden Sie in der Regel eine For Each-Schleife, die eine Sequenz von Werten zurückgibt, oder wenden einen Standardabfrageoperator wie Count oder Max an. Dieser Vorgang wird als verzögerte Ausführung bezeichnet.

' Query execution that results in a sequence of values.
For Each number In evensQuery
    Console.Write(number & " ")
Next

' Query execution that results in a single value.
Dim evens = evensQuery.Count()

Für eine Sequenz von Werten greifen Sie auf die abgerufenen Daten zu, indem Sie die Iterationsvariable in der For Each-Schleife verwenden (number im vorherigen Beispiel). Da die Abfragevariable evensQuery die Abfragedefinition und nicht die Abfrageergebnisse enthält, können Sie eine Abfrage beliebig oft ausführen, indem Sie die Abfragevariable mehrmals verwenden. Sie verfügen beispielsweise über eine Datenbank in Ihrer Anwendung, die ständig durch eine separate Anwendung aktualisiert wird. Nachdem Sie eine Abfrage erstellt haben, die Daten aus dieser Datenbank abruft, können Sie eine For Each-Schleife verwenden, um die Abfrage wiederholt auszuführen und jedes Mal die neuesten Daten abzurufen.

Das folgende Beispiel veranschaulicht die Funktionsweise der verzögerten Ausführung. Nachdem evensQuery2 wie in den vorherigen Beispielen mit einer For Each-Schleife definiert und ausgeführt wurde, werden einige Elemente in der Datenquelle numbers geändert. Anschließend wird evensQuery2 erneut von einer zweiten For Each-Schleife ausgeführt. Die Ergebnisse unterscheiden sich beim zweiten Mal, da die For Each-Schleife die Abfrage unter Verwendung der neuen Werte in numbers erneut ausführt.

Dim numberArray() = {0, 1, 2, 3, 4, 5, 6}

Dim evensQuery2 = From num In numberArray
                  Where num Mod 2 = 0
                  Select num

Console.WriteLine("Evens in original array:")
For Each number In evensQuery2
    Console.Write("  " & number)
Next
Console.WriteLine()

' Change a few array elements.
numberArray(1) = 10
numberArray(4) = 22
numberArray(6) = 8

' Run the same query again.
Console.WriteLine(vbCrLf & "Evens in changed array:")
For Each number In evensQuery2
    Console.Write("  " & number)
Next
Console.WriteLine()

Ausgabe:

Evens in original array:

0 2 4 6

Evens in changed array:

0 10 2 22 8

Sofortige Ausführung

Bei der verzögerten Ausführung von Abfragen wird die Abfragedefinition zur späteren Ausführung in einer Abfragevariablen gespeichert. Bei der unmittelbaren Ausführung wird die Abfrage zum Zeitpunkt der Definition ausgeführt. Die Ausführung wird ausgelöst, wenn Sie eine Methode anwenden, die Zugriff auf einzelne Elemente des Abfrageergebnisses erfordert. Die unmittelbare Ausführung wird häufig mithilfe eines der Standardabfrageoperatoren erzwungen, die einzelne Werte zurückgeben. Beispiele sind Count, Max, Average und First. Diese Standardabfrageoperatoren führen die Abfrage aus, sobald sie angewendet werden, um ein Singleton-Ergebnis zu berechnen und zurückzugeben. Weitere Informationen zu Standardabfrageoperatoren, die einzelne Werte zurückgeben, finden Sie unter Aggregation Operations (Aggregationsvorgänge (Visual Basic)), Element Operations (Elementvorgänge (Visual Basic)) und Quantifier Operations (Quantifizierer-Vorgänge (Visual Basic)).

Die folgende Abfrage gibt eine Anzahl der geraden Zahlen in einem Array aus ganzen Zahlen zurück: Die Abfragedefinition wird nicht gespeichert, numEvens ist eine einfache ganze Zahl (Integer).

Dim numEvens = (From num In numbers
                Where num Mod 2 = 0
                Select num).Count()

Sie können das gleiche Ergebnis mit der Aggregate-Methode erzielen.

Dim numEvensAgg = Aggregate num In numbers
                  Where num Mod 2 = 0
                  Select num
                  Into Count()

Sie können auch die Ausführung einer Abfrage erzwingen, indem Sie die Methode ToList oder ToArray für eine Abfrage (unmittelbar) oder eine Abfragevariable (verzögert) aufrufen, wie im folgenden Code gezeigt:

' Immediate execution.
Dim evensList = (From num In numbers
                 Where num Mod 2 = 0
                 Select num).ToList()

' Deferred execution.
Dim evensQuery3 = From num In numbers
                  Where num Mod 2 = 0
                  Select num
' . . .
Dim evensArray = evensQuery3.ToArray()

In den vorherigen Beispielen ist evensQuery3 eine Abfragevariable, evensList hingegen eine Liste und evensArray ein Array.

Die Verwendung von ToList oder ToArray zum Erzwingen der unmittelbaren Ausführung ist besonders nützlich in Szenarien, in denen Sie die Abfrage sofort ausführen und die Ergebnisse in einem einzelnen Auflistungsobjekt zwischenspeichern möchten. Weitere Informationen zu diesen Methoden finden Sie unter Konvertieren von Datentypen.

Sie können auch mithilfe einer IEnumerable-Methode (etwa der IEnumerable.GetEnumerator-Methode) bewirken, dass eine Abfrage ausgeführt wird.

Weitere Informationen