Grundlagen der LINQ-Syntax - EDV

Werbung
Kapitel 2
Grundlagen der LINQ-Syntax
In diesem Kapitel:
LINQ-Abfragen
Abfrageschlüsselwörter
Verzögerte Auswertung von Abfragen und Auflösung von Erweiterungsmethoden
Abschließende Gedanken zu LINQ-Abfragen
Zusammenfassung
24
29
41
45
48
23
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
24
Kapitel 2: Grundlagen der LINQ-Syntax
Sprachintegrierte Abfrage (Language Integrated Query, LINQ) ermöglicht Entwicklern, Sequenzen von
Elementen (Objekte, Entitäten, Datenbankdatensätze, XML-Knoten usw.) innerhalb ihrer Softwareprojekte
mit einer gemeinsamen Syntax und einer einheitlichen Programmiersprache unabhängig von der Herkunft
der behandelten Elemente abzufragen und zu verwalten. Das Schlüsselfeature von LINQ ist die Integration
in weit verbreitete Programmiersprachen. Diese Integration wird ermöglicht durch die Verwendung einer
für alle Arten von Inhalten gemeinsamen Syntax.
Wie Kapitel 1 erläutert hat, stellt LINQ eine grundlegende Infrastruktur für viele unterschiedliche Implementierungen von Abfragemodulen bereit. Dazu gehören LINQ to Objects, LINQ to SQL, LINQ to DataSet,
LINQ to Entities, LINQ to XML usw. Alle diese Abfrageerweiterungen basieren auf spezialisierten Erweiterungsmethoden und nutzen für die Abfrageausdruckssyntax einen gemeinsamen Satz von Schlüsselwörtern,
die in diesem Kapitel behandelt werden.
Bevor wir uns die einzelnen Schlüsselwörter im Detail ansehen, gehen wir verschiedene Aspekte einer
einzelnen LINQ-Abfrage durch und führen in die grundlegenden Elemente der LINQ-Syntax ein.
LINQ-Abfragen
LINQ basiert auf einem Satz von Abfrageoperatoren, die als Erweiterungsmethoden definiert sind und mit
jedem Objekt arbeiten, das die Schnittstelle IEnumerable<T> oder IQueryable<T> implementiert.
HINWEIS
Die Anhänge B und C enthalten weitere Details zu Erweiterungsmethoden.
Durch dieses Konzept wird LINQ zu einem universellen Abfrageframework, weil viele Auflistungen oder
Typen IEnumerable<T> bzw. IQueryable<T> implementieren und jeder Entwickler seine eigene Implementierung definieren kann. Außerdem ist diese Abfrageinfrastruktur äußerst erweiterbar, wie Sie noch in
Kapitel 12 sehen werden. Mit der Architektur können Entwickler das Verhalten einer Methode basierend auf
dem Typ der abzufragenden Daten spezialisieren. Zum Beispiel besitzen sowohl LINQ to SQL als auch
LINQ to XML spezialisierte LINQ-Operatoren, um relationale Daten bzw. XML-Knoten zu verarbeiten.
Abfragesyntax
Die Einführung in die Abfragesyntax beginnt mit einem einfachen Beispiel. Nehmen Sie an, Sie müssen ein
Array von Objekten eines Typs Developer mit LINQ to Objects abfragen und die Namen der Entwickler
extrahieren, die C# als Hauptprogrammiersprache verwenden. Listing 2.1 zeigt, wie der Code aussehen
könnte.
using System;
using System.Linq;
using System.Collections.Generic;
public class Developer {
public string Name;
public string Language;
public int Age;
}
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
25
LINQ-Abfragen
class App {
static void Main() {
Developer[] developers = new Developer[] {
new Developer {Name = "Paolo", Language = "C#"},
new Developer {Name = "Marco", Language = "C#"},
new Developer {Name = "Frank", Language = "VB.NET"}};
var developersUsingCSharp =
from d in developers
where d.Language == "C#"
select d.Name;
foreach (var item in developersUsingCSharp) {
Console.WriteLine(item);
}
}
}
Listing 2.1 Ein einfacher Abfrageausdruck in C# 3.0
Als Ergebnis liefert dieser Code die Namen Paolo und Marco.
In Visual Basic 2008 lässt sich die gleiche Abfrage (und für den gleichen Typ Developer) mit einer Syntax
ausdrücken, wie sie Listing 2.2 zeigt.
Imports System
Imports System.Linq
Imports System.Collections.Generic
Public Class Developer
Public Name As String
Public Language As String
Public Age As Integer
End Class
Module App
Sub Main()
Dim developers As
New Developer
New Developer
New Developer
New Developer() { _
With {.Name = "Paolo", .Language = "C#"}, _
With {.Name = "Marco", .Language = "C#"}, _
With {.Name = "Frank", .Language = "VB.NET"}}
Dim developersUsingCSharp = _
From d In developers _
Where d.Language = "C#" _
Select d.Name
For Each item in developersUsingCSharp
Console.WriteLine(item)
Next
End Sub
End Module
Listing 2.2 Ein einfacher Abfrageausdruck in Visual Basic 2008
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
26
Kapitel 2: Grundlagen der LINQ-Syntax
Die Syntax der Abfragen (in den Listings 2.1 und 2.2 fett gedruckt) bezeichnet man als Abfrageausdruck. In
manchen LINQ-Implementierungen spricht man bei einer speicherresidenten Darstellung dieser Abfragen
von einer Ausdrucksbaumstruktur. Ein Abfrageausdruck operiert auf einer oder mehreren Informationsquellen, indem einer oder mehrere Abfrageoperatoren entweder aus der Gruppe der Standardabfrageoperatoren oder aus der Gruppe der domänenspezifischen Operatoren angewandt werden. Im Allgemeinen liefert
die Auswertung eines Abfrageausdrucks eine Folge von Werten. Ein Abfrageausdruck wird nur dann ausgewertet, wenn sein Inhalt aufgezählt wird. Kapitel 11 gibt weitere Einzelheiten zu Abfrageausdrücken und
Ausdrucksbaumstrukturen an.
HINWEIS
Im Sinne einer einfachen Darstellung zeigen wir in den folgenden Beispielen nur die C# 3.0-Syntax. Die Visual
Basic 2008-Version dieses Beispiels ist aber der C# 3.0-Version sehr ähnlich.
Diese Abfragen lesen sich fast wie SQL-Anweisungen, auch wenn ihr Format etwas abweicht. Der hier
definierte Beispielausdruck besteht aus einem Auswahlbefehl
select d.Name
der auf eine Gruppe von Elementen angewandt wird
from d in developers
wobei als Ziel der from-Klausel jede Instanz einer Klasse infrage kommt, die die Schnittstelle IEnumerable<T> implementiert. Die Auswahl wendet eine bestimmte Filterbedingung an:
where d.Language == "C#"
Die Sprachcompiler übersetzen diese Klauseln in Aufrufe von Erweiterungsmethoden, die sequenziell auf
das Ziel der Abfrage angewandt werden. Die in der Assembly System.Core.dll untergebrachte Kernbibliothek
von LINQ definiert einen Satz von Erweiterungsmethoden, die nach Ziel und Zweck gruppiert sind. Zum
Beispiel enthält die Assembly eine Klasse Enumerable, die zum Namespace System.Linq gehört. Sie definiert
Erweiterungsmethoden, die sich auf Instanzen von Typen anwenden lassen, die die Schnittstelle IEnumerable<T> implementieren.
Die in der Beispielabfrage definierte Filterbedingung (where) wird in einen Aufruf der WhereErweiterungsmethode der Klasse Enumerable übersetzt. Die beiden Überladungen dieser Methode übernehmen einen Delegaten für eine predicate-Funktion, die die zu überprüfende Filterbedingung beschreibt,
während die Ergebnisdaten partitioniert werden. In diesem Fall ist die filternde predicate-Funktion ein
generischer Delegat. Er übernimmt ein Element vom Typ »T«, d. h. vom selben Typ wie die Instanzen, die in
der zu filternden Aufzählung gespeichert sind. Der Delegat liefert ein boolesches Ergebnis, das die Mitgliedschaft des Elements in der gefilterten Ergebnismenge angibt:
public static IEnumerable<T> Where<T>(
this IEnumerable<T> source,
Func<T, bool> predicate);
Wie aus der Methodensignatur hervorgeht, können Sie diese Methode für jeden Typ aufrufen, der IEnumerable<T> implementiert, folglich auch für das developers-Array des Beispiels:
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
LINQ-Abfragen
27
var filteredDevelopers = developers.Where(delegate (Developer d) {
return (d.Language == "C#");
});
Hier stellt das an die Where-Methode übergebene predicate-Argument einen anonymen Delegaten zu einer
Funktion dar, die für jedes Element des Typs Developer aufgerufen wird, das aus der Quelldatenmenge
(developers) stammt. Der Aufruf der Where-Methode liefert eine Teilmenge von Elementen: alle diejenigen
Elemente, die der predicate-Bedingung genügen.
In C# 3.0 und Visual Basic 2008 kann ein anonymer Delegat in einfacherer Weise – mit einem LambdaAusdruck – definiert werden. Der Code für das Filterbeispiel lässt sich dann wie folgt kompakter formulieren:
var filteredDevelopers = developers.Where(d => d.Language == "C#");
WICHTIG
In den Anhängen B und C finden Sie weitere Einzelheiten zur Syntax von Erweiterungsmethoden, LambdaAusdrücken, anonymen Delegaten usw.
Die select-Anweisung ist ebenfalls eine Erweiterungsmethode (namens Select), die von der Klasse Enumerable bereitgestellt wird. Die Signatur der Methode Select sieht so aus:
public static IEnumerable<TResult> Select<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, TResult> selector);
Das Argument selector ist eine Projektion, die eine Aufzählung von Objekten des Typs TResult zurückgibt.
Die Aufzählung wird von einem Satz von Quellobjekten des Typs TSource abgerufen. Genau wie im obigen
Beispiel lässt sich diese Methode mithilfe eines Lambda-Ausdrucks auf die gesamte developers-Auflistung
anwenden. Es ist auch möglich, die Methode auf der von der Programmiersprache gefilterten Auflistung
(namens filteredDevelopers) aufzurufen, weil es sich dabei immer noch um einen Typ handelt, der IEnumerable<T> implementiert:
var csharpDevelopersNames = filteredDevelopers.Select(d => d.Name);
Aufbauend auf der eben beschriebenen Sequenz von Anweisungen lässt sich die Beispielabfrage ohne
Verwendung der Abfrageausdruckssyntax neu formulieren:
IEnumerable<string> developersUsingCSharp =
developers
.Where(d => d.Language == "C#")
.Select(d => d.Name);
Die Methoden Where und Select übernehmen Lambda-Ausdrücke als Argumente. Diese Lambda-Ausdrücke
werden in Prädikate und Projektionen übersetzt, die auf einem Satz von generischen Delegattypen basieren,
die in der Assembly System.Core.dll des Namespaces System definiert sind.
Die komplette Familie der generischen Delegattypen sieht folgendermaßen aus:
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
28
Kapitel 2: Grundlagen der LINQ-Syntax
public delegate TResult Func< TResult >();
public delegate TResult Func< T, TResult >( T arg );
public delegate TResult Func< T1, T2, TResult > (T1 arg1, T2 arg2 );
public delegate TResult Func< T1, T2, T3, TResult >
( T1 arg1, T2 arg2, T3 arg3 );
public delegate TResult Func< T1, T2, T3, T4, TResult >
(T1 arg1, T2 arg2, T3 arg3, T4 arg4 );
Viele Erweiterungsmethoden der Klasse Enumerable übernehmen diese Delegaten als Argumente und die
Beispiele in diesem Kapitel greifen ebenfalls darauf zurück.
Die endgültige Version der anfänglichen Abfrage könnte wie in Listing 2.3 aussehen.
Func<Developer, bool> filteringPredicate = d => d.Language == "C#";
Func<Developer, string> selectionPredicate = d => d.Name;
IEnumerable<string> developersUsingCSharp =
developers
.Where(filteringPredicate)
.Select(selectionPredicate);
Listing 2.3 Der erste in grundlegende Elemente übersetzte Abfrageausdruck
Genau wie der Visual Basic 2008-Compiler übersetzt der C# 3.0-Compiler die LINQ-Abfrageausdrücke
(Listing 2.1 und Listing 2.2) in eine Anweisung ähnlich der in Listing 2.3. Wenn Sie sich einmal mit der
Abfrageausdruckssyntax (Listing 2.1 und Listing 2.2) vertraut gemacht haben, ist es einfacher und leichter,
die Syntax zu schreiben und zu verwalten, selbst wenn sie optional ist und Sie immer auf die äquivalente –
wortreichere – Version (Listing 2.3) zurückgreifen können. Trotzdem ist es manchmal notwendig, eine
Erweiterungsmethode direkt aufzurufen, weil die Abfrageausdruckssyntax nicht alle möglichen Erweiterungsmethoden abdeckt.
WICHTIG
Kapitel 3 geht ausführlicher auf alle Erweiterungsmethoden ein, die in der Klasse Enumerable des Namespaces
System.Linq definiert sind.
Vollständige Abfragesyntax
Im vorherigen Abschnitt wurde eine einfache Abfrage über eine Liste von Objekten beschrieben. Die
Abfragesyntax ist allerdings umfangreicher und stärker gegliedert als in diesem Beispiel gezeigt. Dabei gibt
es für die Programmiersprachen viele verschiedene Schlüsselwörter, die den meisten Abfrageszenarios
genügen. Jede Abfrage beginnt mit einer from-Klausel und endet entweder mit einer select-Klausel oder
einer group-Klausel. Im Unterschied zur SQL-Syntax beginnt die Abfrage nicht mit einer select-Anweisung,
sondern mit einer from-Klausel. Neben anderen technischen Gründen hängt das vor allem damit zusammen, Microsoft IntelliSense für den restlichen Teil der Abfrage bereitzustellen, um das Formulieren von
Bedingungs-, Auswahl- und anderen Abfrageausdrucksklauseln zu erleichtern. Eine select-Klausel projiziert
das Ergebnis eines Ausdrucks in ein aufzählbares Objekt. Eine group-Klausel projiziert das Ergebnis eines
Ausdrucks basierend auf einer Gruppierungsbedingung in einen Satz von Gruppen, wobei jede Gruppe ein
aufzählbares Objekt darstellt. Der folgende Code zeigt einen Prototyp für die vollständige Abfrageausdruckssyntax:
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
Abfrageschlüsselwörter
29
query-expression ::= from-clause query-body
query-body ::=
join-clause*
(from-clause join-clause* | let-clause | where-clause)*
orderby-clause?
(select-clause | groupby-clause)
query-continuation?
from-clause ::= from itemName in srcExpr
select-clause ::= select selExpr
groupby-clause ::= group selExpr by keyExpr
Auf die erste from-Klausel können null oder mehr from-, let- oder where-Klauseln folgen. Eine let-Klausel
weist dem Ergebnis eines Ausdrucks einen Namen zu. Diese Klausel ist nützlich, wenn Sie denselben Ausdruck mehrmals innerhalb einer Abfrage referenzieren müssen.
let-clause ::= let itemName = selExpr
Wie bereits erwähnt, definiert eine where-Klausel einen Filter, der angewandt wird, um spezifische Elemente
in die Ergebnisse einzuschließen.
where-clause ::= where predExpr
Jede from-Klausel generiert eine lokale Bereichsvariable. Sie entspricht den einzelnen Elementen in der
Quellsequenz, auf die Abfrageoperatoren (wie zum Beispiel die Erweiterungsmethoden von System.Linq.
Enumerable) angewandt werden.
Auf eine from-Klausel können beliebig viele join-Klauseln folgen. Vor der abschließenden select- oder groupKlausel können Sie mit einer orderby-Klausel angeben, wie die Ergebnisse sortiert werden sollen:
join-clause ::=
join itemName in srcExpr on keyExpr equals keyExpr
(into itemName)?
orderby-clause ::= orderby (keyExpr (ascending | descending)?)*
query-continuation ::= into itemName query-body
Das ganze Buch hindurch finden Sie Beispiele für Abfrageausdrücke. Nutzen Sie diesen Abschnitt als Referenz, wenn Sie bestimmte Elemente Ihrer Syntax überprüfen möchten.
Abfrageschlüsselwörter
In den folgenden Abschnitten werden die verschiedenen Abfrageschlüsselwörter behandelt, die in der
Abfrageausdruckssyntax verfügbar sind.
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
30
Kapitel 2: Grundlagen der LINQ-Syntax
Die Klausel from
Das erste Schlüsselwort ist die from-Klausel. Sie definiert die Datenquelle einer Abfrage oder Unterabfrage
und eine Bereichsvariable, die jedes einzelne aus der Datenquelle abzufragende Element definiert. Als
Datenquelle kommt jede Instanz eines Typs infrage, der die Schnittstellen IEnumerable, IEnumerable<T>
oder IQueryable<T> (die IEnumerable<T> implementiert) implementiert. Das folgende Codefragment zeigt
eine Beispielanweisung in C# 3.0, die diese Klausel verwendet.
from rangeVariable in dataSource
Der Sprachcompiler leitet den Typ der Bereichsvariablen aus dem Typ der Datenquelle ab. Ist zum Beispiel
die Datenquelle vom Typ IEnumerable<Developer>, erhält die Bereichsvariable den Typ Developer. In den
Fällen, in denen Sie keine stark typisierte Datenquelle verwenden – beispielsweise eine ArrayList von Objekten des Typs Developer, die IEnumerable implementiert –, sollten Sie den Typ der Bereichsvariablen explizit
angeben. Listing 2.4 zeigt ein Beispiel für eine derartige Abfrage mit einer expliziten Deklaration des Typs
Developer für die Bereichsvariable d.
ArrayList developers = new ArrayList();
developers.Add(new Developer { Name = "Paolo", Language = "C#" });
developers.Add(new Developer { Name = "Marco", Language = "C#" });
developers.Add(new Developer { Name = "Frank", Language = "VB.NET" });
var developersUsingCSharp =
from Developer d in developers
where d.Language == "C#"
select d.Name;
foreach (string item in developersUsingCSharp) {
Console.WriteLine(item);
}
Listing 2.4 Ein Abfrageausdruck für eine nicht generische Datenquelle mit Typdeklaration für die Bereichsvariable
Im obigen Beispiel ist die Typumwandlung obligatorisch. Andernfalls lässt sich die Abfrage nicht kompilieren, weil der Compiler nicht in der Lage ist, den Typ der Bereichsvariablen automatisch abzuleiten. Dabei
geht die Fähigkeit verloren, den Language- und Name-Memberzugriff in derselben Abfrage aufzulösen.
Abfragen können mit mehreren from-Klauseln Verknüpfungen zwischen mehreren Datenquellen definieren. In C# 3.0 verlangt jede Datenquelle die Deklaration einer from-Klausel, wie es in Listing 2.5 zu sehen
ist, das Kunden mit ihren Bestellungen verknüpft. Beachten Sie bitte, dass die Beziehung zwischen Customer
und Order durch die Anwesenheit eines Orders-Arrays vom Typ Order in jeder Instanz von Customer
physisch definiert ist.
WICHTIG
Wenn Sie mehrere from-Klauseln verwenden, wird die Verknüpfungsbedingung durch die Struktur der Daten
bestimmt und unterscheidet sich vom Konzept einer Verknüpfung in einer relationalen Datenbank. (Hierfür müssen Sie die joinKlausel in einem Abfrageausdruck verwenden, was später in diesem Kapitel erläutert wird.)
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
31
Abfrageschlüsselwörter
public class Customer {
public String Name { get; set; }
public String City { get; set; }
public Order[] Orders { get; set; }
}
public class Order {
public Int32 IdOrder { get; set; }
public Decimal EuroAmount { get; set; }
public String Description { get; set; }
}
// ... Code weggelassen ...
static void queryWithJoin() {
Customer[] customers = new Customer[] {
new Customer { Name = "Paolo", City = "Brescia",
Orders = new Order[] {
new Order { IdOrder = 1, EuroAmount = 100,
new Order { IdOrder = 2, EuroAmount = 150,
new Order { IdOrder = 3, EuroAmount = 230,
}},
new Customer { Name = "Marco", City = "Torino",
Orders = new Order[] {
new Order { IdOrder = 4, EuroAmount = 320,
new Order { IdOrder = 5, EuroAmount = 170,
}}};
Description = "Order 1" },
Description = "Order 2" },
Description = "Order 3" },
Description = "Order 4" },
Description = "Order 5" },
var ordersQuery =
from
c in customers
from
o in c.Orders
select new { c.Name, o.IdOrder, o.EuroAmount };
foreach (var item in ordersQuery) {
Console.WriteLine(item);
}
}
Listing 2.5 Ein C# 3.0-Abfrageausdruck mit einer Verknüpfung zwischen einer Reihe von Datenquellen
In Visual Basic 2008 kann eine einzelne from-Klausel mehrere Datenquellen, die jeweils durch Komma
getrennt werden, definieren (siehe Listing 2.6).
Dim customers As Customer() = { _
New Customer With {.Name = "Paolo", .City = "Brescia", _
.Orders = New Order() { _
New Order With {.IdOrder = 1, .EuroAmount = 100,
New Order With {.IdOrder = 2, .EuroAmount = 150,
New Order With {.IdOrder = 3, .EuroAmount = 230,
}}, _
New Customer With {.Name = "Marco", .City = "Torino", _
.Orders = New Order() { _
New Order With {.IdOrder = 4, .EuroAmount = 320,
New Order With {.IdOrder = 5, .EuroAmount = 170,
}}}
.Description = "Order 1"}, _
.Description = "Order 2"}, _
.Description = "Order 3"} _
.Description = "Order 4"}, _
.Description = "Order 5"} _
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
32
Kapitel 2: Grundlagen der LINQ-Syntax
Dim ordersQuery = _
From
c In customers, _
o In c.Orders _
Select c.Name, o.IdOrder, o.EuroAmount
For Each item In ordersQuery
Console.WriteLine(item)
Next
Listing 2.6 Ein Visual Basic 2008-Abfrageausdruck mit einer Verknüpfung zwischen einer Reihe von Datenquellen
Auf Verknüpfungen geht dieses Kapitel später ausführlicher ein.
Die Klausel where
Wie bereits erwähnt, spezifiziert die where-Klausel eine Filterbedingung, die auf die Datenquelle angewendet wird. Das Prädikat wendet eine boolesche Bedingung auf jedes Element in der Datenquelle an, sodass
nur diejenigen Elemente herausgezogen werden, bei denen die Auswertung der Bedingung true ergibt. In
ein und derselben Abfrage können mehrere where-Klauseln oder eine where-Klausel mit mehreren Prädikaten erscheinen, die mit den logischen Operatoren (&&, || und ! in C# 3.0 bzw. And, Or, AndAlso, OrElse, Is
und IsNot in Visual Basic 2008) kombiniert werden. Als Prädikat kommt in Visual Basic 2008 jeder Ausdruck infrage, der sich zu einem booleschen Wert auswerten lässt. Somit können Sie auch einen numerischen Ausdruck verwenden, der als true gilt, wenn er ungleich null ist.
Die Abfrage in Listing 2.7 verwendet eine where-Klausel, um alle Bestellungen mit einem EuroAmount
größer als 200 Euro herauszuziehen.
var ordersQuery =
from c in customers
from o in c.Orders
where o.EuroAmount > 200
select new { c.Name, o.IdOrder, o.EuroAmount };
Listing 2.7 Ein C# 3.0-Abfrageausdruck mit einer where-Klausel
Listing 2.8 zeigt die entsprechende Abfragesyntax in Visual Basic 2008.
Dim ordersQuery = _
From c In customers, _
o In c.Orders _
Where o.EuroAmount > 200 _
Select c.Name, o.IdOrder, o.EuroAmount
Listing 2.8 Ein Visual Basic 2008-Abfrageausdruck mit einer where-Klausel
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
Abfrageschlüsselwörter
33
Die Klausel select
Die select-Klausel spezifiziert die Gestalt der Abfrageausgabe. Sie basiert auf einer Projektion, die bestimmt,
was aus dem Ergebnis der Auswertung aller vorangehenden Klauseln und Ausdrücke auszuwählen ist. In
Visual Basic 2008 ist die Select-Klausel nicht obligatorisch. Ist sie nicht angegeben, gibt die Abfrage einen
Typ zurück, der auf der für den aktuellen Gültigkeitsbereich gekennzeichneten Bereichsvariablen basiert. In
den Listings 2.7 und 2.8 wurde die select-Klausel verwendet, um anonyme Typen zu projizieren, die aus
Eigenschaften oder Membern der Bereichsvariablen im Gültigkeitsbereich bestehen. Ein Vergleich der C#
3.0-Syntax (Listing 2.7) und der Visual Basic 2008-Syntax (Listing 2.8) zeigt, dass die Visual Basic 2008Syntax im select-Muster mehr einer SQL-Anweisung ähnelt, während die C# 3.0-Version eher die Syntax der
Programmiersprache erkennen lässt. In der Tat müssen Sie in C# 3.0 explizit Ihre Absicht deklarieren, um
eine neue Instanz eines anonymen Typs zu erstellen, während die Sprachsyntax in Visual Basic 2008 schlanker ist und die inneren Abläufe verbirgt.
Die Klauseln group und into
Mit der group-Klausel können Sie ein Ergebnis projizieren, das nach einem Schlüssel gruppiert wurde. Die
Klausel lässt sich als Alternative zur from-Klausel einsetzen und erlaubt es, einzelne oder mehrere Schlüsselwerte zu verwenden. Listing 2.9 zeigt ein Beispiel für eine Abfrage, die Entwickler nach ihrer Programmiersprache gruppiert.
Developer[] developers = new Developer[] {
new Developer { Name = "Paolo", Language = "C#" },
new Developer { Name = "Marco", Language = "C#" },
new Developer { Name = "Frank", Language = "VB.NET" },
};
var developersGroupedByLanguage =
from d in developers
group d by d.Language;
foreach (var group in developersGroupedByLanguage) {
Console.WriteLine("Language: {0}", group.Key);
foreach (var item in group) {
Console.WriteLine("\t{0}", item.Name);
}
}
Listing 2.9 Ein C# 3.0-Abfrageausdruck, um Entwickler nach der Programmiersprache zu gruppieren
Das Codefragment nach Listing 2.9 liefert die folgende Ausgabe:
Language: C#
Paolo
Marco
Language: VB.NET
Frank
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
34
Kapitel 2: Grundlagen der LINQ-Syntax
Wie das Codebeispiel zeigt, ist das Ergebnis der Abfrage eine Aufzählung von Gruppen, die durch einen
Schlüssel identifiziert werden und aus inneren Elementen bestehen. Praktisch können Sie jede Gruppe im
Ergebnis der Abfrage aufzählen, ihre Key-Eigenschaft auf der Konsole ausgeben und die Elemente in jeder
Gruppe durchsuchen, um ihre Werte herauszuziehen. Wie bereits weiter oben erwähnt, können Sie Elemente nach einem Mehrfachschlüsselwert, der anonyme Typen verwendet, gruppieren. Listing 2.10 zeigt ein
Beispiel, das die Entwickler nach Sprache und Alter gruppiert.
Developer[] developers =
new Developer { Name
new Developer { Name
new Developer { Name
};
new Developer[] {
= "Paolo", Language = "C#", Age = 32 },
= "Marco", Language = "C#", Age = 37},
= "Frank", Language = "VB.NET", Age = 48 },
var developersGroupedByLanguage =
from d in developers
group d by new { d.Language, AgeCluster = (d.Age / 10) * 10 };
foreach (var group in developersGroupedByLanguage) {
Console.WriteLine("Language: {0}", group.Key);
foreach (var item in group) {
Console.WriteLine("\t{0}", item.Name);
}
}
Listing 2.10 Ein C# 3.0-Abfrageausdruck, um Entwickler nach Programmiersprache und Alter zu gruppieren
Dieses Mal sieht die Ausgabe des Codefragments gemäß Listing 2.10 wie folgt aus:
Language: { Language = C#, AgeCluster = 30 }
Paolo
Marco
Language: { Language = VB.NET, AgeCluster = 40 }
Frank
In diesem Beispiel ist der Schlüssel (Key) für jede Gruppe ein anonymer Typ, der durch zwei Eigenschaften
definiert wird: Language und AgeCluster.
Visual Basic 2008 unterstützt auch die Gruppierung der Ergebnisse mithilfe der Group By-Klausel. Das
Beispiel in Listing 2.11 zeigt eine Abfrage, die der in Listing 2.9 äquivalent ist.
Dim developers As
New Developer
New Developer
New Developer
Developer()
With {.Name
With {.Name
With {.Name
=
=
=
=
{ _
"Paolo", .Language = "C#", .Age = 32}, _
"Marco", .Language = "C#", .Age = 37}, _
"Frank", .Language = "VB.NET", .Age = 48}}
Dim developersGroupedByLanguage = _
From d In developers _
Group d By d.Language Into Group _
Select Language, Group
For Each group In developersGroupedByLanguage
Console.WriteLine("Language: {0}", group.Language)
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
Abfrageschlüsselwörter
35
For Each item In group.Group
Console.WriteLine("
{0}", item.Name)
Next
Next
Listing 2.11 Ein Visual Basic 2008-Abfrageausdruck, um Entwickler nach der Programmiersprache zu gruppieren
Die Visual Basic 2008-Syntax ist etwas komplexer als die entsprechende C# 3.0-Syntax. In Visual Basic 2008
müssen Sie die Gruppierung mithilfe der Into-Klausel projizieren, um ein neues Group-Objekt der Elemente
zu erstellen, und dann explizit das Auswahlmuster deklarieren. Allerdings lässt sich das Ergebnis der Gruppierung einfacher aufzählen, weil der Key-Wert seinen Namen (Language) beibehält.
C# 3.0 bietet ebenfalls eine into-Klausel, die in Verbindung mit dem Schlüsselwort group nützlich, wenn
auch nicht obligatorisch ist. Das Schlüsselwort into können Sie verwenden, um die Ergebnisse einer select-,
group- oder join-Anweisung in einer temporären Variablen zu speichern. Eine derartige Konstruktion bietet
sich an, wenn Sie zusätzliche Abfragen über den Ergebnissen ausführen müssen. Aufgrund dieses Verhaltens
bezeichnet man das Schlüsselwort auch als Fortsetzungsklausel. Listing 2.12 zeigt ein Beispiel für einen C#
3.0-Abfrageausdruck, der die into-Klausel verwendet.
var developersGroupedByLanguage =
from d in developers
group d by d.Language into developersGrouped
select new {
Language = developersGrouped.Key,
DevelopersCount = developersGrouped.Count()
};
foreach (var group in developersGroupedByLanguage) {
Console.WriteLine ("Language {0} contains {1} developers",
group.Language, group.DevelopersCount);
}
Listing 2.12 Ein C# 3.0-Abfrageausdruck, der die into-Klausel verwendet
Die Klausel orderby
Wie der Name vermuten lässt, dient die orderby-Klausel dazu, das Ergebnis einer Abfrage in auf- oder
absteigender Reihenfolge zu sortieren. Die Sortierung lässt sich mit einem oder mehreren Schlüsseln durchführen, die verschiedene Sortierrichtungen kombinieren. Listing 2.13 zeigt ein Beispiel für eine Abfrage, um
die von Kunden aufgegebenen Bestellungen herauszuziehen und nach EuroAmount zu sortieren.
var ordersSortedByEuroAmount =
from
c in customers
from
o in c.Orders
orderby o.EuroAmount
select new { c.Name, o.IdOrder, o.EuroAmount };
Listing 2.13 Ein C# 3.0-Abfrageausdruck mit einer orderby-Klausel
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
36
Kapitel 2: Grundlagen der LINQ-Syntax
Die Beispielabfrage in Listing 2.14 wählt Bestellungen sortiert nach Kundenname (Name) und EuroAmount
in absteigender Reihenfolge aus.
var ordersSortedByCustomerAndEuroAmount =
from
c in customers
from
o in c.Orders
orderby c.Name, o.EuroAmount descending
select new { c.Name, o.IdOrder, o.EuroAmount };
Listing 2.14 Ein C# 3.0-Abfrageausdruck, der eine orderby-Klausel mit mehreren Sortierbedingungen verwendet
Listing 2.15 zeigt die entsprechende Abfrage als Visual Basic 2008-Version.
Dim ordersSortedByCustomerAndEuroAmount = _
From
c In customers, _
o In c.Orders _
Order By c.Name, o.EuroAmount Descending _
Select c.Name, o.IdOrder, o.EuroAmount
Listing 2.15 Ein Visual Basic 2008-Abfrageausdruck, der eine orderby-Klausel mit mehreren Sortierbedingungen verwendet
Hier weisen beide Sprachen eine sehr ähnliche Syntax auf.
Die Klausel join
Mit dem Schlüsselwort join können Sie unterschiedliche Datenquellen verbinden, und zwar auf der Basis
eines Members, der sich auf Gleichheit prüfen lässt. Die Klausel funktioniert ähnlich wie eine SQLGleichheitsverknüpfung. Es ist allerdings nicht möglich, die zu verknüpfenden Elemente mit Vergleichsoperatoren der Art größer als, kleiner als oder ungleich zu vergleichen. Die Gleichheitstests definieren Sie mit
einem speziellen Schlüsselwort equals, das ein anderes Verhalten als der Operator == zeigt, weil die Position
der Operanden eine Rolle spielt. Bei equals verwendet der linke Schlüssel die äußere Quellsequenz und der
rechte Schlüssel die innere Quelle. Der Gültigkeitsbereich der äußeren Quelle beschränkt sich auf die linke
Seite von equals und die innere Quellsequenz ist nur auf der rechten Seite gültig. In Pseudocode sieht dieses
Konzept wie folgt aus:
join-clause ::= join innerItem in innerSequence on outerKey equals innerKey
Mithilfe der join-Klausel können Sie innere Verknüpfungen, Gruppenverknüpfungen und linke äußere
Verknüpfungen definieren. Eine innere Verknüpfung gibt eine lineare Ergebniszuordnung der äußeren
Datenquellenelemente zur entsprechenden inneren Datenquelle zurück. Dabei werden die äußeren Datenquellenelemente, die keine Entsprechung bei den inneren Datenquellenelementen haben, übersprungen.
Listing 2.16 gibt eine einfache Abfrage mit einer inneren Verknüpfung zwischen Artikelkategorien und dazu
gehörenden Produkten an.
public class Category {
public Int32 IdCategory { get; set; }
public String Name { get; set; }
}
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
37
Abfrageschlüsselwörter
public class Product {
public String IdProduct { get; set; }
public Int32 IdCategory { get; set; }
public String Description { get; set; }
}
// ... Code weggelassen ...
Category[] categories = new Category[] {
new Category { IdCategory = 1, Name = "Pasta"},
new Category { IdCategory = 2, Name = "Beverages"},
new Category { IdCategory = 3, Name = "Other food"},
};
Product[] products = new Product[] {
new Product { IdProduct = "PASTA01", IdCategory
new Product { IdProduct = "PASTA02", IdCategory
new Product { IdProduct = "PASTA03", IdCategory
new Product { IdProduct = "BEV01", IdCategory =
new Product { IdProduct = "BEV02", IdCategory =
};
= 1, Description
= 1, Description
= 1, Description
2, Description =
2, Description =
= "Tortellini" },
= "Spaghetti" },
= "Fusilli" },
"Water" },
"Orange Juice" },
var categoriesAndProducts =
from
c in categories
join
p in products on c.IdCategory equals p.IdCategory
select new {
c.IdCategory,
CategoryName = c.Name,
Product = p.Description
};
foreach (var item in categoriesAndProducts) {
Console.WriteLine(item);
}
Listing 2.16 Ein C# 3.0-Abfrageausdruck mit einer inneren Verknüpfung
Diese Ausgabe dieses Codefragments sieht etwa wie folgt aus. Beachten Sie, dass die Kategorie Other food
fehlt, weil sie keine Artikel enthält.
{
{
{
{
{
IdCategory
IdCategory
IdCategory
IdCategory
IdCategory
=
=
=
=
=
1,
1,
1,
2,
2,
CategoryName
CategoryName
CategoryName
CategoryName
CategoryName
=
=
=
=
=
Pasta, Product = Tortellini }
Pasta, Product = Spaghetti }
Pasta, Product = Fusilli }
Beverages, Product = Water }
Beverages, Product = Orange Juice }
Eine Gruppenverknüpfung liefert eine hierarchische Ergebnismenge, bei der die inneren Sequenzelemente
mit ihren korrespondierenden äußeren Sequenzelementen gruppiert werden. Fehlen für ein Element der
äußeren Sequenz korrespondierende innere Sequenzelemente, wird das äußere Element mit einem leeren
Array verknüpft. Eine Gruppenverknüpfung besitzt aufgrund ihres hierarchischen Ergebnisses keine Entsprechung in der SQL-Syntax. Listing 2.17 zeigt ein Beispiel für eine derartige Abfrage. (In Kapitel 3 lernen
Sie eine erweiterte Form dieses Abfragetyps kennen.)
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
38
Kapitel 2: Grundlagen der LINQ-Syntax
var categoriesAndProducts =
from c in categories
join p in products on c.IdCategory equals p.IdCategory
into productsByCategory
select new {
c.IdCategory, CategoryName = c.Name,
Products = productsByCategory
};
foreach (var category in categoriesAndProducts) {
Console.WriteLine("{0} - {1}", category.IdCategory, category.CategoryName);
foreach (var product in category.Products) {
Console.WriteLine("\t{0}", product.Description);
}
}
Listing 2.17 Ein C# 3.0-Abfrageausdruck mit einer Gruppenverknüpfung
Dieses Mal ist die Kategorie Other food in der Ausgabe vorhanden, auch wenn sie leer ist:
1 – Pasta
Tortellini
Spaghetti
Fusilli
2 – Beverages
Water
Orange Juice
3 - Other food
In Visual Basic 2008 steht das spezielle Schlüsselwort Group Join zur Verfügung, um Gruppenverbindungen
in Abfrageausdrücken zu definieren.
Eine linke äußere Verknüpfung liefert eine lineare Ergebnismenge, die alle äußeren Quellelemente umfasst,
selbst wenn das entsprechende innere Quellelement fehlt. Um dieses Ergebnis zu erzeugen, brauchen Sie die
Erweiterungsmethode DefaultIfEmpty, die einen Standardwert zurückgibt, falls ein Datenquellenwert leer
ist. Auf diese und viele andere Erweiterungsmethoden geht Kapitel 3 näher ein. Listing 2.18 zeigt ein
Beispiel für diese Syntax.
var categoriesAndProducts =
from c in categories
join p in products on c.IdCategory equals p.IdCategory
into productsByCategory
from pc in productsByCategory.DefaultIfEmpty(
new Product {
IdProduct = String.Empty,
Description = String.Empty,
IdCategory = 0})
select new {
c.IdCategory,
CategoryName = c.Name,
Product = pc.Description
};
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
39
Abfrageschlüsselwörter
foreach (var item in categoriesAndProducts) {
Console.WriteLine(item);
}
Listing 2.18 Ein C# 3.0-Abfrageausdruck mit einer linken äußeren Verknüpfung
Dieses Beispiel erzeugt die folgende Ausgabe auf der Konsole:
{
{
{
{
{
{
IdCategory
IdCategory
IdCategory
IdCategory
IdCategory
IdCategory
=
=
=
=
=
=
1,
1,
1,
2,
2,
3,
CategoryName
CategoryName
CategoryName
CategoryName
CategoryName
CategoryName
=
=
=
=
=
=
Pasta, Product = Tortellini }
Pasta, Product = Spaghetti }
Pasta, Product = Fusilli }
Beverages, Product = Water }
Beverages, Product = Orange Juice }
Other food, Product = }
Die Kategorie Other food ist mit einem leeren Artikel vertreten. Diesen Wert stellt die Erweiterungsmethode
DefaultIfEmpty bereit.
Schließlich sei zur join-Klausel erwähnt, dass Sie Elemente mithilfe von zusammengesetzten Schlüsseln
vergleichen können. Verwenden Sie einfach anonyme Typen, wie es für das Schlüsselwort group gezeigt
wurde. Wenn zum Beispiel ein zusammengesetzter Schlüssel in Category aus IdCategory und Year besteht,
können Sie die folgende Anweisung schreiben, wobei in der equals-Bedingung ein anonymer Typ verwendet
wird.
from c in categories
join p in products
on new { c.IdCategory, c.Year } equals new { p.IdCategory, p.Year }
into productsByCategory
Wie Sie in diesem Kapitel bereits gesehen haben, können Sie die Ergebnisse von Verknüpfungen auch mit
verschachtelten from-Klauseln erhalten. Dieser Ansatz bietet sich an, wenn Sie Abfragen mit NichtGleichheitsverknüpfungen (Non-Equijoins) definieren müssen. Die Syntax von Visual Basic 2008 ähnelt der
von C# 3.0, bietet aber auch einige Shortcuts, um Verknüpfungen schneller zu definieren. Es lassen sich
implizite Verknüpfungsanweisungen definieren, indem mehrere In-Klauseln in der From-Anweisung verwendet und Gleichheitsbedingungen mit einer Where-Klausel definiert werden. Ein Beispiel für diese
Syntax ist in Listing 2.19 zu sehen.
Dim categoriesAndProducts = _
From c In categories, p In products _
Where c.IdCategory = p.IdCategory _
Select c.IdCategory, CategoryName = c.Name, Product = p.Description
For Each item In categoriesAndProducts
Console.WriteLine(item)
Next
Listing 2.19 Eine implizite Verknüpfungsanweisung in Visual Basic 2008
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
40
Kapitel 2: Grundlagen der LINQ-Syntax
Listing 2.20 zeigt die gleiche Abfrage, die hier aber mit der normalen expliziten join-Syntax formuliert ist.
Dim categoriesAndProducts = _
From
c In categories Join p In products _
On p.IdCategory Equals c.IdCategory _
Select c.IdCategory, CategoryName = c.Name, Product = p.Description
Listing 2.20 Eine explizite Verknüpfungsanweisung in Visual Basic 2008
Beachten Sie, dass in Visual Basic 2008 die Reihenfolge der Elemente im Gleichheitstest keine Rolle spielt,
weil der Compiler sie in eigener Regie anordnet. Dadurch ist die Abfragesyntax etwas »entspannter« als sie
vom herkömmlichen relationalen SQL bekannt ist.
Die Klausel let
Mit der let-Klausel ist es möglich, das Ergebnis eines Unterausdrucks in einer Variablen zu speichern, die
sich dann an anderer Stelle in der Abfrage verwenden lässt. Diese Klausel ist nützlich, wenn Sie den gleichen
Ausdruck mehrmals in derselben Abfrage verwenden müssen und ihn nicht jedes Mal neu definieren
möchten. Mit der let-Klausel definieren Sie für diesen Ausdruck eine Bereichsvariable und verweisen darauf
innerhalb der Abfrage. Nachdem Sie die in der let-Klausel definierte Bereichsvariable zugewiesen haben,
können Sie sie nicht mehr ändern. Wenn jedoch die Bereichsvariable einen abfragbaren Typ speichert, kann
sie abgefragt werden. Das Beispiel in Listing 2.21 wendet diese Klausel an, um dieselben Artikelkategorien
mit der Anzahl ihrer Artikel auszuwählen und nach der Anzahl selbst zu sortieren.
var categoriesByProductsNumberQuery =
from
c in categories
join
p in products on c.IdCategory equals p.IdCategory
into productsByCategory
let
ProductsCount = productsByCategory.Count()
orderby ProductsCount
select new { c.IdCategory, ProductsCount};
foreach (var item in categoriesByProductsNumberQuery) {
Console.WriteLine(item);
}
Listing 2.21 Beispiel in C# 3.0 für die let-Klausel
Die Ausgabe des obigen Codefragments sieht wie folgt aus:
{ IdCategory = 3, ProductsCount = 0 }
{ IdCategory = 2, ProductsCount = 2 }
{ IdCategory = 1, ProductsCount = 3 }
Visual Basic 2008 verwendet eine ähnliche Syntax wie C# 3.0 und erlaubt es ebenfalls, mehrere durch
Komma getrennte Aliasnamen innerhalb derselben let-Klausel zu definieren.
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
Verzögerte Auswertung von Abfragen und Auflösung von Erweiterungsmethoden
41
Zusätzliche Schlüsselwörter in Visual Basic 2008
Visual Basic 2008 umfasst zusätzliche Schlüsselwörter für Abfrageausdrücke, die in C# 3.0 nur über Erweiterungsmethoden verfügbar sind. Die folgende Liste beschreibt diese Schlüsselwörter:
Aggregate wendet eine Aggregatfunktion auf eine Datenquelle an. Mit diesem Schlüsselwort können Sie
statt mit einer From-Klausel eine neue Abfrage beginnen.
Distinct beseitigt doppelte Werte in Abfrageergebnissen.
Skip überspringt die ersten n Elemente eines Abfrageergebnisses.
Skip While überspringt von einem Abfrageergebnis die ersten Elemente, die einem angegebenen Prädikat entsprechen.
Take nimmt die ersten n Elemente eines Abfrageergebnisses.
Take While nimmt von einem Abfrageergebnis die ersten Elemente, die einem angegebenen Prädikat
entsprechen.
Die Schlüsselwörter Skip und Take bzw. Skip While und Take While können zusammen verwendet werden,
um die Abfrageergebnisse zu paginieren. Kapitel 3 zeigt einige Beispiele zu diesem Thema.
Mehr zur Abfragesyntax
Mittlerweile kennen Sie alle Abfrageschlüsselwörter, die über die Programmiersprachen verfügbar sind.
Denken Sie dabei daran, dass jeder Abfrageausdruck vom Sprachcompiler in einen Aufruf der entsprechenden Erweiterungsmethoden konvertiert wird. Wenn Sie eine Datenquelle mithilfe von LINQ abfragen
möchten und für eine bestimmte Operation in einem Abfrageausdruck kein Schlüsselwort vorhanden ist,
können Sie native oder benutzerdefinierte Erweiterungsmethoden in Verbindung mit der Abfrageausdruckssyntax direkt aufrufen. Falls Sie ausschließlich Erweiterungsmethoden verwenden (wie es in Listing
2.3 zu sehen ist), spricht man von Methodensyntax. Wird die Abfragesyntax in Verbindung mit Erweiterungsmethoden eingesetzt (wie in Listing 2.17 gezeigt), bezeichnet man das Ergebnis als gemischte Abfragesyntax.
Verzögerte Auswertung von Abfragen und Auflösung
von Erweiterungsmethoden
In diesem Abschnitt geht es um zwei Verhaltensweisen eines Abfrageausdrucks: verzögerte Abfrageauswertung und Auflösung von Erweiterungsmethoden. Beide Konzepte sind für alle LINQ-Implementierungen
wichtig.
Verzögerte Abfrageauswertung
Ein Abfrageausdruck wird nicht ausgewertet, wenn er definiert wird, sondern erst, wenn er verwendet wird.
Sehen Sie sich dazu das Beispiel in Listing 2.22 an.
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
42
Kapitel 2: Grundlagen der LINQ-Syntax
List<Developer> developers
new Developer { Name =
new Developer { Name =
new Developer { Name =
});
= new List<Developer>(new Developer[] {
"Paolo", Language = "C#", Age = 32 },
"Marco", Language = "C#", Age = 37},
"Frank", Language = "VB.NET", Age = 48 },
var query =
from d in developers
where d.Language == "C#"
select new { d.Name, d.Age };
Console.WriteLine("There are {0} C# developers.", query.Count());
Listing 2.22 Beispiel für eine LINQ-Abfrage über einer Gruppe von Entwicklern
Dieser Code deklariert eine sehr einfache Abfrage, die lediglich zwei Elemente enthält. Das können Sie am
Code ablesen, der die Liste der Entwickler deklariert, oder anhand der Konsolenausgabe des Codes, der die
Erweiterungsmethode Count aufruft:
There are 2 C# developers.
Nehmen Sie nun an, dass Sie den Inhalt der Quellsequenz ändern möchten, indem Sie eine neue DeveloperInstanz hinzufügen – nachdem die Variable query definiert worden ist (siehe Listing 2.23).
developers.Add(new Developer {
Name = "Roberto", Language = "C#", Age = 35 });
Console.WriteLine("There are {0} C# developers.", query.Count());
Listing 2.23 Beispielcode, um die Gruppe der abzufragenden Entwickler zu ändern
Wenn Sie die Variable query erneut aufzählen oder einfach ihre Elementanzahl überprüfen, wie es in Listing
2.23 geschieht, nachdem ein neuer Entwickler hinzugefügt wurde, erhalten Sie als Ergebnis drei Elemente.
Der neue Entwickler ist jetzt im Ergebnis enthalten, obwohl er erst nach der Definition von query hinzugefügt wurde.
Dieses Verhalten ist darin begründet, dass ein Abfrageausdruck vom logischen Standpunkt her eine Art von
»Abfrageplan« beschreibt. Ausgeführt wird er erst, wenn er verwendet wird, und er wird immer wieder
ausgeführt, wenn Sie ihn aufrufen. Manche LINQ-Implementierungen – wie zum Beispiel LINQ to Objects
– implementieren dieses Verhalten über Delegaten. Andere Implementierungen – wie zum Beispiel LINQ to
SQL – verwenden Ausdrucksbaumstrukturen, die sich auf die Schnittstelle IQueryable<T> stützen. Diese so
genannte verzögerte Abfrageauswertung ist ein fundamentales Konzept in LINQ, und zwar unabhängig von
der verwendeten LINQ-Implementierung.
Verzögerte Abfrageauswertung ist nützlich, weil Sie Abfragen einmalig definieren und mehrmals anwenden
können: Wenn sich die Quellsequenz geändert hat, wird das Ergebnis immer mit dem neuesten Inhalt
aktualisiert. Betrachten Sie aber eine Situation, in der Sie einen Snapshot des Ergebnisses zu einem bestimmten Sicherungspunkt erstellen möchten, um es mehrmals zu verwenden und die erneute Ausführung
aus Performancegründen zu vermeiden, oder um unabhängig von Änderungen an der Quellsequenz zu
sein. In diesem Fall müssen Sie eine Kopie des Ergebnisses erstellen. Dazu können Sie einen Satz so genann-
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
Verzögerte Auswertung von Abfragen und Auflösung von Erweiterungsmethoden
43
ter Konvertierungsoperatoren (wie zum Beispiel ToArray, ToList, ToDictionary und ToLookup) verwenden,
die genau für diesen Zweck ausgelegt sind. Kapitel 3 beschäftigt sich ausführlich mit den Konvertierungsoperatoren.
Auflösung von Erweiterungsmethoden
Die Auflösung von Erweiterungsmethoden gehört zu den wichtigsten Konzepten, die Sie verstehen müssen,
um LINQ zu beherrschen. Sehen Sie sich den Code in Listing 2.24 an. Er definiert eine benutzerdefinierte
Liste vom Typ Developer (namens Developers) und eine Klasse DevelopersExtension mit einer Erweiterungsmethode Where, die speziell für Instanzen des Typs Developers konzipiert ist.
public sealed class Developers : List<Developer> {
public Developers(IEnumerable<Developer> items) : base(items) { }
}
public static class DevelopersExtension {
public static IEnumerable<Developer> Where(
this Developers source, Func<Developer, bool> predicate) {
Console.WriteLine("Invoked Where extension method for Developers");
return (source.AsEnumerable().Where(predicate));
}
public static IEnumerable<Developer> Where(
this Developers source,
Func<Developer, int, bool> predicate) {
Console.WriteLine("Invoked Where extension method for Developers");
return (source.AsEnumerable().Where(predicate));
}
}
Listing 2.24 Beispielcode, um die Gruppe der abzufragenden Entwickler zu modifizieren
Die einzige spezielle Aufgabe in den benutzerdefinierten Where-Erweiterungsmethoden besteht darin, mit
einer Ausgabe auf der Konsole anzuzeigen, dass die Methoden aufgerufen wurden. Danach wird die Anforderung an die Where-Erweiterungsmethoden übergeben, die für jede Standardinstanz des Typs IEnumerable<T> definiert sind, wobei die Quelle mit einer Methode AsEnumerable (auf die Kapitel 3 eingeht)
konvertiert wird.
Wenn Sie das schon bekannte developers-Array verwenden, ist das Verhalten der Abfrage in Listing 2.25
recht interessant.
Developers developers = new Developers(new Developer[] {
new Developer { Name = "Paolo", Language = "C#", Age = 32 },
new Developer { Name = "Marco", Language = "C#", Age = 37},
new Developer { Name = "Frank", Language = "VB.NET", Age = 48 },
});
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
44
Kapitel 2: Grundlagen der LINQ-Syntax
var query =
from d in developers
where d.Language == "C#"
select d;
Console.WriteLine("There are {0} C# developers.", query.Count());
Listing 2.25 Ein Abfrageausdruck über einer benutzerdefinierten Liste vom Typ Developers
Der Compiler konvertiert den Abfrageausdruck in den folgenden Code, wie Sie es bereits weiter vorn in
diesem Kapitel gesehen haben:
Var expert =
developers
.Where (d => d.Language == "C#")
.Select(d => d);
Durch die Anwesenheit der Klasse DevelopersExtension handelt es sich bei der Erweiterungsmethode Where
um diejenige, die von DevelopersExtension definiert wird, und nicht um die universelle Version, die in
System.Linq.Enumerable definiert ist. (Um als Containerklasse für Erweiterungsmethoden zu gelten, muss
die Klasse DevelopersExtension als static deklariert und im aktuellen – oder in einem per using-Direktiven in
den aktiven Namespace eingebundenen – Namespace definiert sein.) Der vom Compiler generierte Code,
der die Erweiterungsmethoden auflöst, sieht wie folgt aus:
var expr =
Enumerable.Select(
DevelopersExtension.Where(
developers,
d => d.Language == "C#"),
d=> d );
Letztlich werden immer die statischen Methoden einer statischen Klasse aufgerufen, doch die erforderliche
Syntax ist mit Erweiterungsmethoden kompakter und intuitiver als die Verwendung der weitschweifigeren
expliziten statischen Methodenaufrufe.
Jetzt erfahren Sie die wirkliche Leistung von LINQ. Mithilfe von Erweiterungsmethoden konnten Sie
benutzerdefinierte Verhaltensweisen für spezifische Typen definieren. In den folgenden Kapiteln werden
LINQ to SQL, LINQ to XML und andere Implementierungen von LINQ erläutert. Dabei handelt es sich
dank der von den Compilern realisierten Auflösung von Erweiterungsmethoden lediglich um spezifische
Implementierungen von Abfrageoperatoren.
An diesem Punkt sieht alles prima aus. Stellen Sie sich aber nun vor, dass Sie die benutzerdefinierte Liste
vom Typ Developers mit der standardmäßigen und nicht mit der spezialisierten Where-Erweiterungsmethode abfragen müssen. Die benutzerdefinierte Liste sollten Sie in eine verallgemeinerte Liste konvertieren, um die vom Compiler vorgenommene Auflösung der Erweiterungsmethode umzuleiten. Auch dieses
Szenario kann von den Konvertierungsoperatoren – die in Kapitel 3 behandelt werden – profitieren.
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
Abschließende Gedanken zu LINQ-Abfragen
45
Abschließende Gedanken zu LINQ-Abfragen
In diesem Abschnitt geht es um einige weitere Details, die das Degenerieren von Abfrageausdrücken und die
Ausnahmebehandlung betreffen.
Abfrageausdrücke degenerieren
Manchmal muss man die Elemente einer Datenquelle durchlaufen, ohne dass Filtern, Sortieren, Gruppieren
oder benutzerdefiniertes Projizieren erforderlich ist. Sehen Sie sich zum Beispiel die in Listing 2.26 angegebene Abfrage an.
Developer[] developers = new Developer[] {
...
};
var query =
from d in developers
select d;
foreach (var developer in query) {
Console.WriteLine(developer.Name);
}
Listing 2.26 Ein degenerierter Abfrageausdruck über einer Liste des Typs Developers
In diesem Beispiel durchläuft der Code einfach die Datenquelle, sodass sich die Frage stellt, weshalb die
Datenquelle nicht direkt verwendet wird, wie es in Listing 2.27 geschieht.
Developer[] developers = new Developer[] {
...
};
foreach (var developer in developers) {
Console.WriteLine(developer.Name);
}
Listing 2.27 Iteration über einer Liste des Typs Developers
Augenscheinlich sind die Ergebnisse von Listing 2.26 und Listing 2.27 gleich. Allerdings stellt der Abfrageausdruck in Listing 2.26 sicher, dass die benutzerdefinierte Methode aufgerufen wird, wenn eine spezifische
Select-Erweiterungsmethode für die Datenquelle existiert, und das Ergebnis mit dem einer Übersetzung des
Abfrageausdrucks in seine entsprechende Methodensyntax übereinstimmt.
Eine Abfrage, die einfach ein Ergebnis liefert, das der ursprünglichen Datenquelle gleicht (und somit trivial
oder nutzlos scheint), wird als degenerierter Abfrageausdruck bezeichnet. Wenn Sie andererseits die Datenquelle direkt durchlaufen (wie in Listing 2.27), umgehen Sie den Aufruf irgendeiner benutzerdefinierten
Select-Erweiterungsmethode und das korrekte Verhalten ist nicht garantiert, sofern Sie nicht ausdrücklich
die Datenquelle ohne Verwendung von LINQ durchlaufen möchten.
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
46
Kapitel 2: Grundlagen der LINQ-Syntax
Ausnahmebehandlung
Abfrageausdrücke können innerhalb ihrer Definition auf externe Methoden verweisen. Die Methoden
müssen nicht immer fehlerfrei ausgeführt werden. Die in Listing 2.28 definierte Abfrage ruft die Methode
DoSomething über jedem Element der Datenquelle auf.
static Boolean DoSomething(Developer dev) {
if (dev.Age > 40)
throw new ArgumentOutOfRangeException("dev");
return (dev.Language == "C#");
}
static void Main() {
Developer[] developers = new Developer[] {
...
new Developer { Name = "Frank", Language = "VB.NET", Age = 48 },
};
var query =
from d in developers
let
SomethingResult = DoSomething(d)
select new { d.Name, SomethingResult };
foreach (var item in query) {
Console.WriteLine(item);
}
}
Listing 2.28 Ein C# 3.0-Abfrageausdruck, der auf einer externen Methode basiert, die eine fiktive Ausnahme auslöst
Die Methode DoSomething löst eine fiktive Ausnahme aus für jeden Entwickler, der älter als 40 Jahre ist. Der
Aufruf dieser Methode erfolgt innerhalb der Abfrage. Erreicht die Schleife während der Abfrageausführung
das Element mit dem Entwickler Frank, der 48 Jahre alt ist, löst die benutzerdefinierte Methode eine Ausnahme aus.
Überlegen Sie genau, ob Sie benutzerdefinierte Methoden in Abfragedefinitionen aufrufen möchten, weil
dies eine gefährliche Angelegenheit ist. Davon können Sie sich beim Ausführen dieses Beispielcodes selbst
überzeugen. Wenn Sie aber externe Methoden aufrufen wollen, ist es am besten, die Auflistung des Abfrageergebnisses in einen try...catch-Block einzuhüllen. Wie Sie eben im Abschnitt »Verzögerte Abfrageauswertung« gesehen haben, wird ein Abfrageausdruck jedes Mal ausgewertet, wenn er aufgezählt wird, und nicht,
wenn er definiert wird. Dementsprechend zeigt Listing 2.29, wie Sie den Code von Listing 2.28 in der
richtigen Weise schreiben.
Developer[] developers = new Developer[] {
...
new Developer { Name = "Frank", Language = "VB.NET", Age = 48 },
};
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
Abschließende Gedanken zu LINQ-Abfragen
47
var query =
from d in developers
let
SomethingResult = DoSomething(d)
select new { d.Name, SomethingResult };
try {
foreach (var item in query) {
Console.WriteLine(item);
}
}
catch (ArgumentOutOfRangeException e) {
Console.WriteLine(e.Message);
}
Listing 2.29 Ein C# 3.0-Abfrageausdruck mit Ausnahmebehandlung
Im Allgemeinen ist es nutzlos, die Definition eines Abfrageausdrucks in einen try...catch-Block einzuschließen. Darüber hinaus sollten Sie aus dem gleichen Grund vermeiden, die Ergebnisse der Methoden oder
Konstruktoren direkt als Datenquellen für einen Abfrageausdruck zu verwenden. Weisen Sie stattdessen
ihre Ergebnisse Instanzvariablen zu und hüllen Sie die Variablenzuweisung in einen try...catch-Block ein,
wie es in Listing 2.30 geschehen ist.
static void queryWithExceptionHandledInDataSourceDefinition() {
Developer[] developers = null;
try {
developers = createDevelopersDataSource();
}
catch (InvalidOperationException e) {
// Annehmen, dass die Methode createDevelopersDataSource
// im Fehlerfall eine InvalidOperationException auslöst
// Ausnahme behandeln ...
Console.WriteLine(e.Message);
}
if (developers != null)
{
var query =
from d in developers
let
SomethingResult = DoSomething(d)
select new { d.Name, SomethingResult };
try {
foreach (var item in query) {
Console.WriteLine(item);
}
}
catch (ArgumentOutOfRangeException e) {
Console.WriteLine(e.Message);
}
}
}
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
48
Kapitel 2: Grundlagen der LINQ-Syntax
private static Developer[] createDevelopersDataSource() {
// Fiktive InvalidOperationException ausgelöst
throw new InvalidOperationException();
}
Listing 2.30 Ein C# 3.0-Abfrageausdruck mit Ausnahmebehandlung in der Deklaration lokaler Variablen
Zusammenfassung
In diesem Kapitel wurden die Prinzipien der Abfrageausdrücke und ihre unterschiedlichen Syntaxvarianten
(Abfragesyntax, Methodensyntax und gemischte Syntax) sowie alle wichtigen Schlüsselwörter, die in C# 3.0
und Visual Basic 2008 verfügbar sind, behandelt. Dabei haben Sie zwei wichtige LINQ-Features kennen
gelernt: verzögerte Auswertung von Abfragen und Auflösung von Erweiterungsmethoden. Außerdem hat
dieses Kapitel Beispiele von degenerierten Abfrageausdrücken gezeigt und erläutert, wie Sie Ausnahmen bei
der Auflistung von Abfrageausdrücken behandeln. Im nächsten Kapitel geht es dann detailliert um LINQ to
Objects.
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
Einführung
In diesem Abschnitt:
Über dieses Buch
Systemanforderungen
Die Companion-Website
Support für dieses Buch
XX
XXII
XXII
XXII
XIX
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
XX
Einführung
Dieses Buch behandelt die sprachintegrierte Abfrage (Language Integrated Query, LINQ) umfassend und
tiefgehend. Es soll Ihnen in erster Linie ein geschlossenes Bild vermitteln, was LINQ ist und was Sie mit
LINQ tun bzw. nicht tun können. Das Buch richtet sich an .NET-Entwickler, die in Microsoft .NET 2.0
sattelfest sind und sich die Frage stellen, ob sie ihr Fachwissen auf Microsoft .NET 3.5 aktualisieren sollten.
Um mit LINQ zu arbeiten, müssen Sie auf Ihrem Entwicklungscomputer Microsoft .NET Framework 3.5
und Microsoft Visual Studio 2008 installieren.
Das Buch wurde auf Basis der RTM (Released to Market)-Edition von LINQ und Microsoft .NET 3.5
geschrieben. Allerdings befinden sich bestimmte Komplexe wie zum Beispiel LINQ to Entities, das
ADO.NET Entity Framework und Parallel LINQ immer noch im Betastadium. Gegenüber dem derzeitigen
Release könnten beim endgültigen Release einige Features dieser Technologien geändert, entfernt oder
hinzugefügt werden. Auf der Website http://www.programminglinq.com/ verwalten wir eine Änderungsliste,
einen Revisionsverlauf, Korrekturen und ein Blog zu LINQ im Allgemeinen und zu diesem Buch im Besonderen. Außerdem finden Sie auf der Webseite http://www.programminglinq.com/booklinks.aspx alle im Buch
genannten URLs (sortiert nach der Seitennummer), sodass Sie diese URLs nicht manuell kopieren müssen.
Über dieses Buch
Dieses Buch ist in fünf Teile mit insgesamt 18 Kapiteln und 3 Anhängen gegliedert.
Als Einsteiger in C# 3.0 und/oder Visual Basic 2008 sollten Sie zunächst Anhang B bzw. Anhang C lesen.
Diese Anhänge beschäftigen sich mit den neuen Features, die diese Sprachen einführen, um vollständige
Unterstützung für LINQ zu bieten. Sind Sie bereits mit diesen neuen Sprachversionen vertraut, können Sie
diese Anhänge als Referenz nutzen, falls Sie bei der Syntax in Bezug auf LINQ unsicher sind. In unseren
Beispielen verwenden wir hauptsächlich C#, doch sind fast alle vorgestellten LINQ-Features auch in Visual
Basic 2008 verfügbar. Wenn es zweckmäßig ist, verwenden wir Visual Basic 2008, weil es einige Features zu
bieten hat, die in C# 3.0 nicht zur Verfügung stehen.
Der erste Teil des Buchs »Grundlagen von LINQ« führt LINQ ein, erläutert die Syntax und liefert alle
erforderlichen Informationen, damit Sie LINQ mit speicherinternen Objekten verwenden können. Mit
LINQ to Objects sollten Sie sich als Erstes vertraut machen, denn viele Features davon begegnen Ihnen in
den anderen LINQ-Implementierungen, die in diesem Buch beschrieben werden. Die ersten drei Kapitel des
ersten Teils sollten Sie auf jeden Fall lesen.
Der zweite Teil dieses Buchs, »LINQ und relationale Daten«, ist allen LINQ-Implementierungen gewidmet,
die Zugriff auf relationale Datenspeicher bieten. Die LINQ to SQL-Implementierung gliedert sich in drei
Kapitel. Kapitel 4 »LINQ to SQL: Daten abfragen« erläutert die Grundlagen, wie Sie relationale Daten den
LINQ-Entitäten zuordnen und LINQ-Abfragen erstellen, die in SQL-Abfragen transformiert werden. In
Kapitel 5 »LINQ to SQL: Daten verwalten« erfahren Sie, wie Sie mit den Entitäten von LINQ to SQL Änderungen an Daten behandeln, die aus einer Datenbank extrahiert wurden. Kapitel 6 »Tools für LINQ to SQL«
ist ein Führer zu den Tools, die Sie dabei unterstützen, Datenmodelle für LINQ to SQL zu definieren. Wenn
Sie LINQ to SQL in Ihren Anwendungen einsetzen möchten, lesen Sie am besten alle entsprechenden
Kapitel.
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
Über dieses Buch
XXI
Kapitel 7 »LINQ to DataSet« beschäftigt sich mit der Implementierung von LINQ, die sich auf ADO.NETDataSets richtet. Wenn Sie in einer Anwendung DataSets verwenden, erfahren Sie in diesem Kapitel, wie Sie
LINQ integrieren, oder zumindest, wie Sie stufenweise von DataSets zu einem Domänenmodell übergehen,
das mit LINQ to SQL oder LINQ to Entitites behandelt wird.
Kapitel 8 »LINQ to Entities« beschreibt die LINQ-Implementierung, die den Zugriff auf das ADO.NET
Entity Framework einhüllt. Dieses Kapitel sollten Sie nach dem LINQ to SQL-Kapitel lesen, weil auf die
Konzepte, die bei den beiden Implementierungen ähnlich sind, später wieder Bezug genommen wird. In
diesem Kapitel gehen wir davon aus, dass Sie über das ADO.NET Entity Framework Bescheid wissen. Falls
Sie nicht über genügend Erfahrung verfügen, können Sie sich zuerst mit dem entsprechenden Anhang
befassen.
Der dritte Teil »LINQ und XML« besteht aus zwei LINQ to XML-Kapiteln: Kapitel 9 »LINQ to XML: Infoset
verwalten« und Kapitel 10 »LINQ to XML: Knoten abfragen«. Lesen Sie diese Kapitel am besten zuerst,
bevor Sie Code entwickeln, der in irgendeiner Form Daten in XML liest oder manipuliert.
Die komplexesten Themen des Buchs sind im vierten Teil »LINQ erweitert« versammelt. In Kapitel 11
»Ausdrucksbaumstrukturen intern« lernen Sie, wie Sie eine Ausdrucksbaumstruktur verarbeiten, erstellen
und lesen. Kapitel 12 »LINQ erweitern« erläutert, wie Sie LINQ erweitern, indem Sie eigene Datenstrukturen erstellen, einen vorhandenen Dienst einhüllen und schließlich einen benutzerdefinierten LINQAnbieter erstellen. Eine LINQ-Schnittstelle zum Parallel Framework für .NET wird in Kapitel 13 »LINQ
parallel« beschrieben. Schließlich bietet Kapitel 14 »Andere LINQ-Implementierungen« einen Überblick
über die wichtigsten LINQ-Komponenten, die von Drittanbietern zur Verfügung stehen. Die einzelnen
Kapitel in diesem Teil können Sie unabhängig von den anderen lesen. Lediglich Kapitel 12 verweist auf ein
anderes Kapitel in diesem Abschnitt, und zwar auf Kapitel 11.
Der fünfte Teil »LINQ im Einsatz« ist der Verwendung von LINQ in unterschiedlichen Szenarios einer
verteilten Anwendung gewidmet. Kapitel 15 »LINQ in einer Multitier-Lösung« dürfte für jeden Leser
interessant sein, weil es sich um ein sehr architekturbezogenes Kapitel handelt, das Ihnen dabei hilft, die
richtigen Entwurfsentscheidungen für Ihre Anwendungen zu treffen. Die Kapitel 16, 17 und 18 präsentieren
relevante Informationen über die Verwendung von LINQ mit vorhandenen Bibliotheken wie zum Beispiel
ASP.NET, Windows Presentation Foundation, Silverlight und Windows Communication Foundation. Wir
empfehlen, dass Sie Kapitel 15 lesen, bevor Sie sich mit den Details der spezifischen Bibliotheken befassen.
Von den Kapiteln 16, 17 und 18 können Sie auch ein oder mehrere Kapitel überspringen, wenn Sie die
jeweilige Technologie nicht verwenden.
Zusätzliche Inhalte online finden
Wenn neue oder aktualisierte Zusatzinformationen zu Ihrem Buch verfügbar werden, stellen wir sie auf
der Microsoft Press Online Developer Tools-Website online. Dabei wird es sich unter anderem um aktualisierte Buchinhalte, Artikel, Links zu begleitenden Inhalten, Errata und Beispielkapitel handeln. Die Website ist unter http://www.microsoft.com/learning/books/online/developer zu finden und wird regelmäßig
aktualisiert.
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
XXII
Einführung
Systemanforderungen
Damit Sie mit LINQ arbeiten und den von uns bereitgestellten Beispielcode verwalten können, sind folgende Anforderungen zu erfüllen:
Unterstützte Betriebssysteme: Microsoft Windows Server 2003, Windows Server 2008, Windows Vista,
Windows XP mit Service Pack 2
Microsoft Visual Studio 2008
Die Companion-Website
Zu diesem Buch gehört eine Companion-Website, von der Sie den im Buch verwendeten Code herunterladen können. Der Code ist nach Themen organisiert. Die Adresse der Companion-Website lautet:
http://www.microsoft.com/mspress/companion/9780735624009.
Alternativ können Sie die Beispieldateien auch unter http://www.microsoft-press.de/support.asp herunterladen. Geben Sie dazu die Nummer 428 in das untere Eingabefeld auf dieser Webseite ein und klicken Sie
auf Suchen.
Support für dieses Buch
Es wurden große Anstrengungen unternommen, um die Fehlerfreiheit für das Buch und die Beispieldateien
zu gewährleisten. Sofern doch einmal Korrekturen oder zusätzliche Hinweise zu diesem Buch oder den
zugehörigen Beispieldateien notwendig waren, so finden Sie diese im World Wide Web unter
http://www.microsoft-press.de/support.asp
Falls Sie Kommentare, Fragen oder Anregungen zu diesem Buch haben, senden Sie sie bitte an folgende
Microsoft Press-Adresse:
[email protected]
Beachten Sie bitte, dass über diese Mailadresse kein Softwareservice angeboten wird. Für Supportinformationen bezüglich der Softwareprodukte besuchen Sie bitte die Microsoft-Website:
http://www.microsoft.com/germany/support.
Paolo Pialorsi, Marco Russo: Datenbankprogrammierung mit Microsoft LINQ. Microsoft Press 2008 (ISBN 978-3-86645-428-6)
Herunterladen