Technische Universität München LINQ – Verstehen und Einsetzen Proseminar Objektorientiertes Programmieren mit .NET und C# Georg Wagner Institut für Informatik Software & Systems Engineering Technische Universität München Agenda 1 2 3 4 5 17.12.10 LINQ-Einführung (Motivation, Bestandteile) C#-Spracherweiterungen (kleine Kontrollfrage) LINQ-Abfragearten LINQ unter der Lupe (Verzögerte Ausführung) LINQ to SQL LINQ – Verstehen und Einsetzen v. Georg Wagner 2 Technische Universität München 1. Einführung zu LINQ 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 3 Technische Universität München LINQ – Die universelle Abfragesprache Language INtegrated Queries 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 4 Technische Universität München Motivationstreiber zur Einführung von LINQ Es wird aus verschiedenen Datenquellen ermittelt, wer heute Geburtstag hat: Datenquellen: <Student> <Name> <Birthday> <MatrNr> </Student> … MS-SQL DB DataSet Programmiersprache: LINQ WIEDER ANDERER CODE Ein einheitlicher Zugriffscode Ergebnis: Heute haben Geburtstag: Herbert, Anton, Fritz… 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 5 Technische Universität München LINQ-Bestandteile Programmiersprachen C# VB.NET F# LINQ-Bausteine Abfrageausdrücke Standard Abfrageoperatoren Expression Trees LINQ-Provider LINQ2Objects Datenquellen LINQ2XML <Student> <Vorname> <Nachname> <MatrNr> </Student> … LINQ2SQL LINQ2Datase t MS-SQL DB DataSet 17.12.10 LINQ2Entities LINQ – Verstehen und Einsetzen v. Georg Wagner ADO.NET Entity Framework 6 Technische Universität München Agenda 1 2 3 4 5 17.12.10 LINQ-Einführung (Motivation, Bestandteile) C#-Spracherweiterungen (kleine Kontrollfrage) LINQ-Abfragearten LINQ unter der Lupe (Verzögerte Ausführung) LINQ to SQL LINQ – Verstehen und Einsetzen v. Georg Wagner 7 Technische Universität München 2. C#-Spracherweiterungen Spracherweiterungen zum Verstehen von LINQ 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 8 Technische Universität München Implizit typisierte lokale Variable Der Compiler leitet für die var-Deklaration implizit den Variablentyp aus der Initialisierung der Variable ab. " „var“ darf nur lokal verwendet werden. Objekt-Initialisierer Objekt-Initialisierer Objekt-Initialisierer nehmen Schreibarbeit ab und initialisieren öffentliche Felder bzw. öffentliche Eigenschaften nach ihrer Instanziierung. 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 9 Technische Universität München Anonyme Typen Zweck:Schnelles Gruppieren von Daten in ein Objekt, ohne dass man explizit eine Klasse deklarieren muss: 1. Intern entsteht ein Typ mit den Eigenschaften Platz, Vorname und Nachname, der sog. Anonyme Typ. 2. Dieser Typ wird mit rangliste.Platz, rangliste.Vorname usw. initialisiert. 3. Am Schluss erhält sprinter die Referenz zum Anonymen Typen. Äquivalent ist auch: 17.12.10 (Namen der Eigenschaften werden übernommen) LINQ – Verstehen und Einsetzen v. Georg Wagner 10 Technische Universität München Erweiterungsmethoden Mit Erweiterungsmethoden kann man bereits definierte Typen nachträglich Methoden hinzufügen. Kennzeichnet welcher Typ zu erweitern ist. … Wenn man nun mit Instanzen arbeitet die IEnumerable<TSource> implementieren (wie z.B. List<Student>) kann man die Erweiterungsmethode abrufen. IntelliSense zeigt sie mit einem blauen Pfeil an: 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 11 Technische Universität München Lambda-Ausdrücke Mit Lambda-Ausdrücken wird das Einfügen von Code an Stellen erlaubt, wo man eigentlich eine Methoden-Übergabe an ein Delegate erwartet hätte. … … Eingabe-Parameter (Typ wird selbst abgeleitet) – Muss mit den Parametern des Delegates Func (hier: TSource) entsprechen. 17.12.10 Lambda-Operator Ausdruck oder Anweisungsblock Rückgabetyp muss mit dem des Delegates (hier: Boolean) übereinstimmen. LINQ – Verstehen und Einsetzen v. Georg Wagner 12 Technische Universität München Kontroll-Frage 2 Parameter deklariert … nur 1 Parameter übergeben Warum funktioniert der Where-Aufruf obwohl nur der 2. Parameter übergeben wurde? Wenn man auf das, was man zugreift (den Typ bei this), noch als Parameter angeben würde, hätte man immer eine unnötige redundante Information. 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 13 Technische Universität München Agenda 1 2 3 4 5 17.12.10 LINQ-Einführung (Motivation, Bestandteile) C#-Spracherweiterungen (kleine Kontrollfrage) LINQ-Abfragearten LINQ unter der Lupe (Verzögerte Ausführung) LINQ to SQL LINQ – Verstehen und Einsetzen v. Georg Wagner 14 Technische Universität München 3. LINQ-Abfragearten 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 15 Technische Universität München LINQ-Abfrageausdrücke Das Datenobjekt studenten wird durch einen LINQ-Abfrageausdruck ausgefiltert: " „Suche alle Studenten, die unser Proseminar machen und sortiere sie dem Vornamen nach absteigend geordnet.“ vom Typ IEnumerable<Student> § Jede Abfrage beginnt mit from und endet mit select (oder group). „studenten“ repräsentiert ein Objekt, das vom Typ IEnumerable<Student> ist § Jedes Objekt, das IEnumerable<T> implementiert, wird im LINQ-Vokabular „Sequenz“ genannt. § studenten kann somit jedes beliebige Objekt sein, dass IEnumerable<T> implementiert (z.B. Array, List, Dictionary, …) 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 16 Technische Universität München LINQ-Abfrageausdrücke implizit typisierte lokale Variable Iterator vom Typ IEnumerable<Student> Um alle gefundene Studenten auszugeben nutzen wir: 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 17 Technische Universität München LINQ-Abfrageoperatoren Lambda-Ausdruck Erweiterungsmethoden Allgemein gilt: IEnumerable<T>-Erweiterungsmethoden geben IEnumerable<T> Typen zurück -> Möglichkeit der Verkettung -> Pipeline Pattern In unserem speziellen Fall ist der Typ jeweils: List<Student> 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 18 Technische Universität München Abfrageausdrücke vs. Abfrageoperatoren Man muss sich gar nicht für eine Art entscheiden, beide sind kombinierbar! - Abfrageausdrücke sind durch Ihre Kompaktheit meistens lesbarer - Aber: Unterstützen nur die Operatoren: 17.12.10 Select GroupBy SelectMany Let Join Cast GroupJoin OrderBy/ThenBy LINQ – Verstehen und Einsetzen v. Georg Wagner 19 Technische Universität München Agenda 1 2 3 4 5 17.12.10 LINQ-Einführung (Motivation, Bestandteile) C#-Spracherweiterungen (kleine Kontrollfrage) LINQ-Abfragearten LINQ unter der Lupe (Verzögerte Ausführung) LINQ to SQL LINQ – Verstehen und Einsetzen v. Georg Wagner 20 Technische Universität München 4. LINQ-Abfragen unter der Lupe 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 21 Technische Universität München LINQ-Abfragen unter der Lupe Schauen wir uns ein einfacheres Beispiel genauer an: Um die Ergebnisse auszugeben haben wir verwendet: Da „teilnehmer“ mit „person“ durchiteriert wird, enthält „teilnehmer“ bereits alle ausgefilterten Ergebnisse. FALSCH: „teilnehmer“ enthält im Vorhinein noch keine Ergebnisse. 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 22 Technische Universität München LINQ-Abfragen unter der Lupe Bei der Kompilierung wird die Abfrage so repräsentiert: = Verzögerte " Ausführung Jedes mal, wenn der Iterator person von teilnehmer ein Element verlangt, wird der obige Code ausgeführt. 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 23 Technische Universität München Verzögerte Ausführung von Abfragen Verzögerte Ausführung erreicht man durch sog. Co-Routinen. Where und Select gehören zu solchen. Where-Methode (Co-Routine): 1. 2. 3. 4. Aufrufer der Co-Routine: Co-Routinen werden mit yield return gekennzeichnet. yield return gibt nicht IEnumerable<TSource> sondern ein Element von TSource zurück Nach jedem yield return wird die Kontrolle dem Aufrufer zurückgegeben Bei weiteren Aufrufe mit demselben Iterator person wird die Co-Routine am letzten yield return fortgesetzt. Dies geschieht hier so lange, bis in source kein element mehr existiert. 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 24 Technische Universität München Verzögerte Ausführung von Abfragen Select() iteriert in einer foreach-Schleife Ergebnisse von Where(). Gibt Where() mit yield return ein Element zurück, gibt Select() dieses Element ebenso mit yield return ein Element zurück Durch Verzögerte Ausführung bei großen Datensatzmengen muss bei der ersten Iteration nicht lange gewartet werden und erfolgt keine große Zwischenspeicherung Dieser Effekt geht durch Sortier, Gruppier oder Aggregier-Erweiterungsmethoden verloren 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 25 Technische Universität München Agenda 1 2 3 4 5 17.12.10 LINQ-Einführung (Motivation, Bestandteile) C#-Spracherweiterungen (kleine Kontrollfrage) LINQ-Abfragearten LINQ unter der Lupe (Verzögerte Ausführung) LINQ to SQL LINQ – Verstehen und Einsetzen v. Georg Wagner 26 Technische Universität München 5. LINQ To SQL Mappen, Abfragen, Verändern 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 27 Technische Universität München Beispiel vom vorigen Vortrag in LINQ SELECT DISTINCT c.[CustomerID] ,c.[CompanyName] FROM [Northwind].[dbo].[Customers] c JOIN [Northwind].[dbo].[Orders] o on c.CustomerID = o.CustomerID JOIN [Northwind].[dbo].[Employees] e on o.EmployeeID = e.EmployeeID WHERE e.EmployeeID = ‘1‘ ODER BY c.[CustomerID] „Ermittle alle verschiedene Firmenkunden mit Ihren Kunden-IDs, die Aufträge bei uns haben die unser Mitarbeiter mit der Firmen-ID 9 bearbeitet, und ordne sie nach deren Kunden-IDs.“ Zur Realisation dieser Anfrage in LINQ brauchen wir: Eine Klasse, die LINQAbfragen in äquivalente SQL-Abfragen des Ziel-Dialektes übersetzt. Lösung: Verwendung der DataContext Klasse aus System.Data.Linq 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 28 Technische Universität München ADO.NET Beispiel vom vorigen Vortrag in LINQ Bevor wir mittels DataContext Daten aus SQL-Tabellen lesen können, müssen wir das relationale Modell auf das Objektmodell mappen. D.h. wir müssen in unserem Fall folgende Zuordnungen machen: Tabelle Klasse Eigenschaft Attribut Mapping-Möglichkeiten: Attribute innerhalb " Klassen 17.12.10 LINQ – Verstehen und Einsetzen XMLDateien v. Georg Wagner LINQ to SQL Designer 29 Technische Universität München Objektrelationales Mapping via Klassen-Attributen Zum Mappen werden die Klassen in System.Data.Linq.Mapping verwendet Klasse Customer wird der SQL-Tabelle Customer zugeordnet – Namen müssen übereinstimmen! Klassen-Eigenschaften werden zu den Attributen der SQL-Tabelle Customer zugeordnet – Namen müssen übereinstimmen! 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 30 Technische Universität München Der Code um der eigentlichen LINQ Abfrage Eine Verbindung zu einem SQL-Server wird hergestellt Tabellen werden noch " nicht eingelesen (Verzögert Ausführung) … Ab der ersten Iteration wird eine Anfrage in SQL-Syntax zum SQLServer geschickt 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 31 Technische Universität München Die LINQ-Abfrage Anfrage, die wir in LINQ realisieren sollten: SELECT DISTINCT c.[CustomerID] ,c.[CompanyName] FROM [Northwind].[dbo].[Customers] c JOIN [Northwind].[dbo].[Orders] o on c.CustomerID = o.CustomerID JOIN [Northwind].[dbo].[Employees] e on o.EmployeeID = e.EmployeeID WHERE e.EmployeeID = ‚1' ODER BY c.[CustomerID] Die LINQ-Anfrage: Objekt-Initialisierer da nur ein Objekt zurückgegeben werden kann, wird ein anonymer Typ definiert. 17.12.10 Da das Distinct-Schlüsselwort in Abfrageausdrücken nicht vorhanden ist, wird auf LINQ-Abfrageoperatoren zurückgegriffen LINQ – Verstehen und Einsetzen v. Georg Wagner 32 Technische Universität München Die LINQ-Abfrage Die LINQ-Anfrage: Zum SQL-Server wird gesendet (ab ersten foreach-Iteration): SELECT DISTINCT [t0].[CustomerID], [t0].[CompanyName] FROM [dbo].[Customers] AS [t0] INNER JOIN [dbo].[Orders] AS [t1] ON [t0].[CustomerID] = [t1].[CustomerID] INNER JOIN [dbo].[Employees] AS [t2] ON [t1].[EmployeeID] = [t2].[EmployeeID] WHERE [t2].[EmployeeID] = 1 Bemerkenswert ist, dass LINQ durch die Angabe des anonymen Typen und des anschließenden Distinct-Operators die Anfrage entsprechend minimiert hat. 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 33 Technische Universität München Aktualisieren von Daten Die Auftrags-Nummern 1 bis 10 wurden in Auftrags-Nr. 123 zusammengeführt. Eine Aktualisierung der Daten ist also nötig. Da die nötige Tabellen schon gemappt sind gestaltet sich eine Daten-Aktualisierung sehr einfach: 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 34 Technische Universität München Quellen • „LINQ Im Einsatz“ – Manning – ISBN: 978-3-446-41429-7 • „Datenbank-Programmierung mit Microsoft LINQ“ – Microsoft Press – ISBN: 9978-3-86645-428 • MSDN 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 35 Technische Universität München Danke für‘s Zuhören! 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 36 Technische Universität München Anhang: Aktualisierungs-Anfrage UPDATE [dbo].[Employees] SET [OrderID] = @p4 WHERE ([EmployeeID] = @p0) AND ([FirstName] = @p1) AND ([LastName] = @p2) AND ([OrderID] = @p3) 17.12.10 LINQ – Verstehen und Einsetzen v. Georg Wagner 37