Link auf die aktuelle Vorlesung im Versionsmanagementsystem GitHub
https://github.com/liaScript/CsharpCourse/blob/master/24_LINQ.md
Die interaktive Form ist unter diese Link zu finden -> LiaScript Vorlesung 24
Wie weit sind wir schon gekommen?
c# Schlüsselwörter:
| abstract | as | base |bool |break |byte |
|case |catch | char |checked |class | const |
|continue |decimal | default | delegate |do |double |
|else |enum | event | explicit | extern |false |
|finally | fixed |float |for |foreach |goto |
|if | implicit | in |int | interface |internal |
| is | lock |long |namespace |new | null |
| object | operator |out | override |params |private |
| protected |public | readonly |ref |return |sbyte |
| sealed |short | sizeof | stackalloc |static |string |
|struct |switch |this |throw |true |try |
| typeof |uint |ulong |unchecked | unsafe |ushort |
|using | virtual |void | volatile |while | |
Auf die Auführung der kontextabhängigen Schlüsselwörter wie where oder
ascending wurde hier verzichtet.
1. Hier stehen jetzt Ihre Fragen ...
-
Die Klausur findet am 23.07.2019, von 12:30 bis 14:30 statt.
-
Bitte senden Sie bis zum Freitag Abend Fragen an die Tutoren, die Sie mit Blick auf die Klausur noch mal besprochen haben wollen.
Dieses Angebot sollte Teil Ihrer Vorbereitungen auf die Prüfung sein und nicht deren Beginn!
-
In die Klausur dürfen Sie einen Notizzettel A4 (bedruckt, beschrieben) mitbringen.
Welcher Art Ihre Notizen sind, ist Ihnen überlassen. Sie sollten sich dafür entscheiden aussagekräftige Beispiele vorzubereiten, die Ihnen Anleitung für die Lösung der Aufgaben gibt.
-
Studentische Mitarbeiter als Tutoren gesucht!
Gegeben sei das Datenset eines Comic-Begeisterten in Form einer generischen
Liste List<T>.
- Bestimmen Sie die Zahl der Einträge unseres Datensatzes
- Filtern Sie die Liste der Comic Figuren nach dem Alter und
- Sortieren Sie die Liste nach dem Anfangsbuchstaben des Namens.
using System;
using System.Collections.Generic;
namespace Rextester
{
public class Character{
protected string name;
public int geburtsjahr;
public Character(string name, int geburtsjahr){
this.name = name;
this.geburtsjahr = geburtsjahr;
}
}
public class ListedCharacter: Character{
public static int Count;
int index;
public ListedCharacter(string name, int geburtsjahr):
base(name, geburtsjahr){
index = Count;
Count = Count + 1;
}
public override string ToString(){
string row = string.Format("|{0,6} | {1,-15} | {2,8} |",
index, name, geburtsjahr);
return row;
}
}
public class Program
{
public static void Main(string[] args){
List<ListedCharacter> ComicHeros = new List<ListedCharacter>{
new ListedCharacter("Spiderman", 1962),
new ListedCharacter("Donald Duck", 1931),
new ListedCharacter("Superman", 1938)
};
Console.WriteLine("Alle Einträge in der Datenbank:");
Console.WriteLine("| Index | Name | Ursprung |");
foreach (ListedCharacter c in ComicHeros){
Console.WriteLine(c);
}
}
}
}@Rextester.eval(@CSharp)
{{1}}
Die Lösung könnte folgendermaßen daher kommen:
Die Dokumentation von List<T> findet sich unter folgendem Link
- Bei der Konsultation der Dokumentation von List "entdecken" Sie die Eigenschaft
Count. - Für die Filteroperation implementieren Sie eine Loop. Sie können dazu
foreachverwenden, weilList<T>das InterfaceIEnumerableimplementiert. - Die Sortieroperation bedingt die Anwendung einer Vergleichsoperation zwischen den Elementen der Liste. Eine Variante ist die Implementierung des Interfaces
IComparablezu diesem Zweck.
using System;
using System.Collections.Generic;
namespace Rextester
{
public class Character{
protected string name;
public int geburtsjahr;
public Character(string name, int geburtsjahr){
this.name = name;
this.geburtsjahr = geburtsjahr;
}
}
public class ListedCharacter: Character, IComparable{
public static int Count;
int index;
public ListedCharacter(string name, int geburtsjahr): base(name, geburtsjahr){
index = Count;
Count = Count + 1;
}
public override string ToString(){
string row = string.Format("|{0,6} | {1,-15} | {2,8} |", index, name, geburtsjahr);
return row;
}
public int CompareTo(object obj){
if (obj == null) return 1;
ListedCharacter otherCharacter = obj as ListedCharacter;
return string.Compare(this.name, otherCharacter.name);
}
}
public class Program
{
public static void Main(string[] args){
List<ListedCharacter> ComicHeros = new List<ListedCharacter>{
new ListedCharacter("Spiderman", 1962),
new ListedCharacter("Donald Duck", 1931),
new ListedCharacter("Superman", 1938)
};
Console.WriteLine("Alle Einträge in der Datenbank:");
Console.WriteLine("| Index | Name | Ursprung |");
foreach (ListedCharacter c in ComicHeros){
Console.WriteLine(c);
}
Console.WriteLine("Gefilterte Einträge in der Datenbank:");
Console.WriteLine("| Index | Name | Ursprung |");
List<ListedCharacter> ComicHerosFiltered = new List<ListedCharacter>();
foreach (ListedCharacter c in ComicHeros){
if (c.geburtsjahr < 1950) ComicHerosFiltered.Add(c);
}
foreach (ListedCharacter c in ComicHerosFiltered){
Console.WriteLine(c);
}
Console.WriteLine("Sortierte Einträge in der Datenbank:");
Console.WriteLine("| Index | Name | Ursprung |");
ComicHeros.Sort();
foreach (ListedCharacter c in ComicHeros){
Console.WriteLine(c);
}
}
}
}@Rextester.eval(@CSharp)
Eine Menge Aufwand für einen simple Operation! Welche zusätzlichen Probleme werden auftreten, wenn Sie eine solche Kette aus Datenerfassung, Verarbeitung und Ausgabe in realen Anwendungen umsetzen?
{{2}}
Die Methoden für den Datenzugriff und die Manipulation abhängig vom Datentyp (Felder, Objektlisten) und der Herkunft (XML-Dokumente, Datenbanken, Excel-Dateien, usw.).LINQ versucht dieses Problem zu beseitigen, indem es innerhalb der Entwicklungsplattform .NET eine einheitliche Methode für jeglichen Datenzugriff zur Verfügung stellt. Die Syntax der Abfragen in LINQ orientiert sich dabei an der Structured Query Language (SQL).
Hier folgt ein kurzer Einschub zum Thema SQL ... um allen Teilnehmern eine sehr grundlegende Sicht zu vermitteln:
SQL ist eine Datenbanksprache zur Definition von Datenstrukturen in relationalen Datenbanken sowie zum Bearbeiten (Einfügen, Verändern, Löschen) und Abfragen von darauf basierenden Datenbeständen.
Ausgangspunkt sind Datenbanktabellen, die Abfragen dienen dabei der Generierung
spezifischer Informationssets:
- "Alle Bücher mit Buchnummern von 123400 bis 123500"
- "Alle Buchnummern mit Autoren, die im 19. Jahrhundert erschienen."
- "In welchem Jahrhundert veröffentlichte welcher Verlag die meisten Bücher?"
- ...
| Buchnummer | Autor | Verlag | Datum | Titel |
|---|---|---|---|---|
| 123456 | Hans Vielschreiber | Musterverlag | 2007 | Wir lernen SQL |
| 123457 | J. Gutenberg | Gutenberg und Co. | 1452 | Drucken leicht gemacht |
| 123458 | Galileo Galilei | Inquisition International | 1640 | Eppur si muove |
| 123459 | Charles Darwin | Vatikan Verlag | 1860 | Adam und Eva |
SQL basiert auf der relationalen Algebra, ihre Syntax ist relativ einfach aufgebaut und semantisch an die englische Umgangssprache angelehnt. Die Bezeichnung SQL bezieht sich auf das englische Wort “query” (deutsch: „Abfrage“). Mit Abfragen werden die in einer Datenbank gespeicherten Daten abgerufen, also dem Benutzer oder einer Anwendersoftware zur Verfügung gestellt. Durch den Einsatz von SQL strebt man die Unabhängigkeit der Anwendungen vom eingesetzten Datenbankmanagementsystem an.
SQL-Aufrufe sind deklarativ, weil der Entwickler hier nur das WAS und nicht das WIE festlegt. Dabei strukturieren sich die Befehle in 4 Kategorien:
- Befehle zur Abfrage und Aufbereitung der gesuchten Informationen
- Befehle zur Datenmanipulation (Ändern, Einfügen, Löschen)
- Befehle zur Definition des Datenbankschemas
- Befehle für die Rechteverwaltung und Transaktionskontrolle.
Eine Datenbanktabelle stellt eine Datenbank-Relation dar. Die Relation ist Namensgeber und Grundlage der relationalen Datenbanken.
{{1-2}}
Erzeugung der Tabellen
MatrNr,Name
26120,Fichte
25403,Jonas
27103,Fauler
CREATE TABLE Student;
INSERT INTO Student SELECT * from ?;MatrNr,Name
26120,Fichte
25403,Jonas
27103,Fauler
@AlaSQL.eval_with_csv
CREATE TABLE hoert;
INSERT INTO hoert SELECT * from ?;MatrNr,VorlNr
26120,5001
25403,5001
27103,5045
@AlaSQL.eval_with_csv
CREATE TABLE Vorlesung;
INSERT INTO Vorlesung SELECT * from ?;VorlNr,Titel,PersNr
5001,ET,15
5022,IT,12
5045,DB,12
@AlaSQL.eval_with_csv
CREATE TABLE Professor;
INSERT INTO Professor SELECT * from ?;PersNr,Name
12,Wirth
15,Tesla
20,Urlauber
@AlaSQL.eval_with_csv
{{2}}
Beispiele
SELECT *
FROM Student;@AlaSQL.eval
SELECT VorlNr, Titel
FROM Vorlesung;@AlaSQL.eval
SELECT DISTINCT MatrNr
FROM hoert;@AlaSQL.eval
SELECT VorlNr, Titel
FROM Vorlesung
WHERE Titel = 'ET';@AlaSQL.eval
LIKE kann mit verschiedenen Platzhaltern verwendet werden: _ steht für ein einzelnes beliebiges Zeichen, % steht für eine beliebige Zeichenfolge. Manche Datenbanksysteme bieten weitere solche Wildcard-Zeichen an, etwa für Zeichenmengen.
ORDER BY öffnet die Möglichkeit die Reihung anzupassen.
SELECT Vorlesung.VorlNr, Vorlesung.Titel, Professor.PersNr, Professor.Name
FROM Professor LEFT OUTER JOIN Vorlesung
ON Professor.PersNr = Vorlesung.PersNr;@AlaSQL.eval
SELECT COUNT(Vorlesung.PersNr) AS Anzahl, Professor.PersNr, Professor.Name
FROM Professor LEFT OUTER JOIN Vorlesung
ON Professor.PersNr = Vorlesung.PersNr
GROUP BY Professor.Name, Professor.PersNr;@AlaSQL.eval
Language Integrated Query (LINQ) umfasst ein Konzept in .NET, dass auf der direkte Integration von Abfragefunktionen abzielt. Dafür definieren die C#, VB.NET und F# eigene Schlüsselwörter sowie eine Menge an vorbestimten LINQ-Methoden. Diese können aber durch den Anwender in der jeweiligen Sprache erweitert werden.
LINQ-Anweisungen sind unmittelbar als Quelltext in .NET-Programme eingebettet. Somit kann der Code durch den Compiler auf Fehler geprüft werden. Andere Verfahren wie ActiveX Data Objects ADO und Open Database Connectivity ODBC hingegen verwenden Abfragestrings. Diese können erst zur Laufzeit interpretiert werden; dann wirken Fehler gravierender und sind schwieriger zu analysieren.
Innerhalb des Quellprogramms in C# oder VB.NET präsentiert LINQ die Abfrage-Ergebnisse als streng typisierte Aufzählungen. Somit gewährleistet es Typsicherheit bereits zur Übersetzungszeit wobei ein minimaler Codeeinsatz zur Realisierung von Filter-, Sortier- und Gruppiervorgänge in Datenquellen investiert wird.
Merkmale von LINQ
-
Die Arbeit mit Abfrageausdrücken ist einfach, da sie viele vertraute Konstrukte der Sprache C# verwenden.
-
Alle Variablen in einem Abfrageausdruck sind stark typisiert, obwohl dieser in der Regel nicht explizit angegeben wird. Der Compiler übernimmt die Ableitung.
-
Eine Abfrage wird erst ausgeführt, wenn Sie über der Abfragevariable iteriert wird. Folglich muss die Quelle in einer iterierbaren Form vorliegen.
-
Zur Kompilierzeit werden Abfrageausdrücke gemäß den in der C#-Spezifikation festgelegten Regeln in Methodenaufrufe des Standardabfrageoperators konvertiert. Die Abfragesyntax ist aber einfacher zu lesen.
-
LINQ kombiniert Abfrageausdrücke und Methodenaufrufe (
countodermax). Hierin liegt die Flexibilität des Konzeptes.
Diese Veranstaltung konzentriert sich auf die LINQ to Objects Realisierung von
LINQ. Dabei können Abfragen mit einer beliebigen IEnumerable- oder IEnumerable<T>-Auflistungen angewandt werden.
public interface IEnumerable<out T> : System.Collections.IEnumerable{
public IEnumerator<T> = GetEnumerator();
}
public interface IEnumerator<out T> : IDisposable,
System.Collections.IEnumerator{
public object Current { get; }
public bool MoveNext ();
public void Reset ();
}Zur Wiederholung soll nochmals ein kurzes Implementierungsbeispiel gezeigt
werden. An dieser Stelle wird eine Klasse myStrings umgesetzt, die als
Enumerationstyp realisiert werden soll. Entsprechend implementiert die Klasse
IEnumerable das Interface IEnumerable<string> und referenziert einen
Enumeratortyp StringEnumerator, der wiederum das Interface generische
Interface IEnumerator<string> umsetzt.
Transformieren Sie folgendes Codefragment in eine UML Darstellung.
using System;
using System.Collections;
using System.Collections.Generic;
namespace Rextester
{
class myStrings : IEnumerable<string>{
public string [] str_arr = new string[] {"one" , "two" ,"three", "four", "five"};
public IEnumerator<string> GetEnumerator()
{
IEnumerator<string> r = new StringEnumerator(this);
return r ;
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator() ;
}
}
class StringEnumerator : IEnumerator<string>{
int index;
myStrings sp;
public StringEnumerator (myStrings str_obj){
index = -1 ;
sp = str_obj ;
}
object IEnumerator.Current{
get
{ return sp.str_arr[ index ] ; }
}
public string Current{
get
{ return sp.str_arr[ index ] ; }
}
public bool MoveNext( ){
if ( index < sp.str_arr.Length - 1 ){
index++ ;
return true ;
}
return false ;
}
public void Reset( ){
index = -1 ;
}
public void Dispose(){
// pass
}
}
class Program {
public static void Main(string[] args){
myStrings spp = new myStrings();
foreach( string i in spp)
System.Console.WriteLine(i);
}
}
}@Rextester.eval(@CSharp)
Welchen Vorteil habe ich verglichen mit einer nicht-enumerate Datenstruktur, zum Beispiel einem array? Im Hinblick auf eine konkrete Implementierung ist zwischen dem Komfort der erweiterten API und den Performance-Eigenschaften abzuwägen.
Einen Überblick dazu bietet unter anderem die Diskussion unter https://stackoverflow.com/questions/169973/when-should-i-use-a-list-vs-a-linkedlist/29263914#29263914
Sie können LINQ zur Abfrage beliebiger aufzählbarer Auflistungen wie List, Array oder Dictionary<TKey,TValue> verwenden. Die Auflistung kann entweder benutzerdefiniert sein oder von einer .NET Framework-API zurückgegeben werden.
Alle LINQ-Abfrageoperationen bestehen aus drei unterschiedlichen Aktionen:
- Abrufen der Datenquelle
- Erstellen der Abfrage
- Ausführen der Abfrage
Für ein einfaches Beispiel, das Filtern einer Liste von Zahlenwerten realisiert sich dies wie folgt:
using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
namespace Rextester
{
class Program {
public static void Main(string[] args){
// Spezifikation der Datenquelle
int[] scores = new int[] { 97, 92, 81, 60 };
// Definition der Abfrage
IEnumerable<int> scoreQuery =
from score in scores // Bezug zur Datenquelle
where score > 80 // Filterkriterium
select score; // "Projektion" des Rückgabewertes
// Execute the query.
foreach (int i in scoreQuery)
{
Console.Write(i + " ");
}
}
}
}@Rextester.eval(@CSharp)
Referenzen
[DatenbankSchema] Wikipedia "SQL", Nils Boßung, https://de.wikipedia.org/wiki/SQL#/media/Datei:SQL-Beispiel.svg
[Mössenböck] Mössenböck, Hanspeter, "Kompaktkurs C#", dpunkt.verlag, 2019
Autoren
Sebastian Zug, André Dietrich

