LINQ - Language Integrated Query 05.01.2014 Ioan Asiminoaei Cuprins Expresii lambda (Lambda expressions). Arbori de expresii (Expression trees). Cuvantul cheie var obiecte si initializarea colectiilor. tipuri anonime, new – forma noua. Metode extinse (Extension methods). Metode partiale (Partial methods). Query expressions.
22
Embed
Cuprins Expresii lambda (Lambda expressions). Arbori de ...iasimin/csharp/C11_LINQ.pdf · LINQ to SQL – API IQuerable ce permite Linq sa lucreze cu SQL Server si nu ...
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
Cuprins
Expresii lambda (Lambda expressions).
Arbori de expresii (Expression trees).
Cuvantul cheie var obiecte si initializarea colectiilor.
tipuri anonime, new – forma noua.
Metode extinse (Extension methods).
Metode partiale (Partial methods).
Query expressions.
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
LINQ - Language Integrated Query = Cereri asupra datelor
LINQ este o tehnologie Microsoft ce furnizeaza un mecanism la nivel de limbaj pentru a executa cereri
de date de orice tip.
Cateva exemple :
Exemplul 1
using System; using System.Linq; string[] greetings = {"hello world", "hello LINQ", "hello Apress"};
var items = from s in greetings where s.EndsWith("LINQ") select s;
foreach (var item in items) Console.WriteLine(item);
Exemplul 2
Se parseaza continutul unui fisier XML ce are urmatoarea structura:
using System; using System.Linq; using System.Xml.Linq; XElement books = XElement.Parse( @"<books> <book> <title>Pro LINQ: Language Integrated Query in C# 2008</title> <author>Joe Rattz</author> </book> <book> <title>Pro WF: Windows Workflow in .NET 3.0</title> <author>Bruce Bukovics</author> </book> <book> <title>Pro C# 2005 and the .NET 2.0 Platform, Third Edition</title> <author>Andrew Troelsen</author> </book> </books>");
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
var titles = from book in books.Elements("book") where (string) book.Element("author") == "Joe Rattz" select book.Element("title");
foreach(var title in titles)
Console.WriteLine(title.Value);
Exemplul 3
string[] numbers = { "0042", "010", "9", "27" }; int[] nums = numbers.Select(s => Int32.Parse(s)).ToArray(); foreach(int num in nums) Console.WriteLine(num);
Exemplul 4
public class Employee {
public int id; public string firstName; public string lastName; public static ArrayList GetEmployees() { ArrayList al = new ArrayList(); al.Add(new Employee { id = 1, firstName = "Joe", lastName = "Rattz"} ); al.Add(new Employee { id = 2, firstName = "William", lastName = "Gates"} ); al.Add(new Employee { id = 3, firstName = "Anders", lastName = "Hejlsberg"} ); return(al); }
}
public class Contact {
public int Id; public string Name; public static void PublishContacts(Contact[] contacts) { foreach(Contact c in contacts) Console.WriteLine("Contact Id: {0} Contact: {1}", c.Id, c.Name); }
Id = e.id, Name = string.Format("{0} {1}", e.firstName, e.lastName)
}).ToArray<Contact>(); PublishContacts(contacts);
Rezultatul este:
Contact Id: 1 Contact: Joe Rattz
Contact Id: 2 Contact: William Gates
Contact Id: 3 Contact: Anders Hejlsberg
LINQ
LINQ este o tehnologie Microsoft ce furnizeaza un mecanism la nivel de limbaj pentru a executa cereri
de date de orice tip. Aceste tipuri includ tablouri si colectii in memorie, baze de date, documente
XML, etc. Printre altele LINQ poate face conversii, poate sorta, poate obtine o submultime a unei
multimi date, etc.
// conversie array de string-uri in array de int. Aceasta este o cerere. string[] numbers = { "0042", "010", "9", "27" }; int[] nums = numbers.Select(s => Int32.Parse(s)).ToArray(); // se face si sortarea int[] nums = numbers.Select(s => Int32.Parse(s)).OrderBy(s => s).ToArray();
In LINQ, multimea de obiecte returnata se numeste sir. Majoritatea sirurilor din LINQ sunt de tip
IEnumerable<T> sau dintr-un tip derivat din IEnumerable<T> , unde T este tipul de data al obiectelor memorate in sir (int, string, double, etc.).
La runtime cand se cer elemente din colectie, are loc un proces de construire a elementelor
submultimii. In acest moment pot aparea exceptii.
Ex. string[] strings = { "Iasi", "Pascani", null, "Harlau" }; Console.WriteLine("Inainte ca Where() sa fie apelat."); // definim multimea ieStrings IEnumerable<string> ieStrings = strings.Where(s => s.Length == 3); Console.WriteLine("Duap ce Where() este apelat."); // Cand se va executa foreach va aparea o exceptie pentru ca // al 3-lea element al sirului are valoarea null, vedeti Where... // Daca nu se extrag elemente din ieStrings nu va aparea exceptia foreach(string s in ieStrings) {
Console.WriteLine("Procesat " + s); }
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
Componente :
1. LINQ to Objects – este numele dat API-ului IEnumerable<T> pentru operatorii de cereri
standard. Putem face cereri asupra tablourilor sau colectiilor din memorie. Operatorii standard
sunt metode statice ale clasei System.Linq.Enumerable.
2. LINQ to XML – API dedicat sa lucreze cu XML. Trebuie sa adaugam o referinta la proiect
System.Xml.Linq.dll si apoi using System.Xml.Linq;
3. LINQ to DataSet – API Linq pentru DataSet-uri.
4. LINQ to SQL – API IQuerable<T> ce permite Linq sa lucreze cu SQL Server si nu numai.
Trebuie referinta la System.Data.Linq.dll si apoi using System.data.Linq ;
5. LINQ to Entities – alternativa la Linq folosit pentru interfata cu bazele de date. Se decupleaza
entitatea model obiect de baza de date (fizic) prin crearea unei logici intre cele doua niveluri.
Trasaturi noi in .NET • Expresii lambda (Lambda expressions) ;
• Arbori de expresii (Expression trees);
• Cuvantul cheie var, obiecte si initializarea colectiilor, tipuri anonime, new – forma noua;
• Metode extinse (Extension methods);
• Metode partiale (Partial methods);
• Expresii cerere (Query expressions).
Metode partiale
Scriem prototipul intr-un loc si codul in alt loc. Daca nu scriem codul atunci compilatorul nu emite cod
pentru acea metoda, iar apelul metodei nu are efect (nu se genereaza exceptie).
Putem sa le folosim cand dezvoltam un tip si nu implementam toate metodele. Le vom implementa
mai tarziu sau altcineva va implementa acele metode dar nu va avea acces la implementarea metodelor
facuta de noi.
Reguli pentru metodele partiale
� Metodele partiale trebuiesc sa fie definite si implementate numai in clase partiale.
� Metodele partiale trebuie sa specifice modificatorul partial.
� Metodele partiale sunt private dar nu trebuie sa specifice modificatorul private, in caz contrar
eroare la compilare.
� Metodele partiale trebuie sa returneze void.
� Metodele partiale pot fi neimplementate.
� Metodele partiale pot fi statice.
� Metodele partiale pot avea argumente.
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
Exemplu namespace PM { partial class A { partial void MetodaPartiala(string s); } // Ceea ce urmeaza trebuie sa fie in alt fisier partial class A { // Daca comentam aceasta linie programul tot se va compila partial void MetodaPartiala(String s) { Console.WriteLine("MetodaPartiala apelata cu: {0}", s); } } }
Se citeste astfel: pentru valorile de intrare date prin parametrii param1, param2, ..., paramN se
va executa codul statement1 ; statement2 ;...statementP iar valoarea returnata este cea
data de return (aici se stabileste si tipul valorii returnate).
In Ex4 tipul valorii de retur (ce este bool) este stabilita de delagate-ul definit in clasa
DelegateAnonim :
public delegate bool IntFilter(int i);
Exemple:
x=> x.Length // tip returnat int x => "Procesat " + x // tip returnat string (x trebuie sa fie string) x => x.Length > 0 // tip returnat bool (x, y) => x == y // tip returnat bool
Observatie:
Expresia lambda trebuie sa accepte in intrare tipurile specificate de delegate si sa returneze acelasi tip
definit de delegate.
Algoritmii complecsi sau reutilizati ar trebui implementati in cadrul metodelor cu nume si nu in cadrul
delegates anonimi sau expresii lambda.
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
Expression Trees
Eexpression trees : data este reprezentata sub forma de arbore ca rezultat al unui operator de cereri,
operator dat sub forma unei expresii lamba.
Evaluarea se face simultan si nu secvential.
Exemplu (nu e sub forma unei cereri – expression trees) forma C#
IEnumerable<T>, secvente si operatorii de cereri standard
IEnumerable<T>, este o interfata pe care o implementeaza toate colectiile generice din C# 2.0.
Aceasta interfata permite enumerarea elementelor din colectie. Vezi Colectii generice de pe pagina
http://www.infoiasi.ro/~iasimin.
O secventa este un termen logic pentru o colectie ce implementeaza interfata IEnumerable<T>.
Daca avem o variabila de tipul IEnumerable<T>, atunci putem spune ca avem un sir (o secventa) de
elemente de tipul T.
Majoritatea operatorilor standard de cereri ( Standard Query Operators ) sunt metode extinse pe clasa
statica System.Linq.Enumerable si au ca prim argument IEnumerable<T>. Din cauza ca sunt
metode extinse, este de preferat sa le apelam pe variabile de tipul IEnumerable<T> in loc de a le
pasa in metoda ca primul argument.
Operatorii Cast<T>() si OfType<T>() – folositi pentru a face conversia colectiilor la IEnumerable<T>.
Se folosesc pentru colectii ce nu implementeaza IEnumerable<T>.
Cast incearca sa faca conversia tuturor elementelor existente si daca exista un element pe care esueaza
va genera o exceptie.
OfType va pune numai acele elemente pentru care conversia reuseste.
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
“Returning IEnumerable<T>, Yielding, and Deferred Queries”
Din cauza ca aceste tipuri de cereri ce returneaza IEnumerable<T> definesc numai prototipul cererii,
putem scrie codul ce defineste cererea o singura data si folosim aceasta cerere de mai multe ori in
enumerarea elementelor.
// Creaza un tablou de int. int[] intArray = new int[] { 1,2,3 }; IEnumerable<int> ints = intArray.Select(i => i);
// Afiseaza rezultatele. foreach(int i in ints) Console.WriteLine(i);
// Schimba un element in sursa de date. intArray[0] = 5; Console.WriteLine("---------");
// Afiseaza rezultatele din nou. foreach(int i in ints) Console.WriteLine(i);
Am creat cererea o singura data si o apelam de mai multe ori. Daca nu ar fi asa, rezultatul enumerarii
ar fi fost identic in cele doua cazuri.
Daca vrem ca cererea sa nu fie « amanata » la executie putem folosi unul din operatorii de conversie
ce nu returneaza un IEnumerable<T>.
Acestia sunt : ToArray, ToList, ToDictionary sau ToLookup.
Exemplu:
// Creaza un tablou de int. int[] intArray = new int[] { 1, 2, 3 }; List<int> ints = intArray.Select(i => i).ToList(); // Afiseaza rezultatele. foreach(int i in ints) Console.WriteLine(i); // Schimba un element in sursa de date. // Modificarea nu este vazuta pentru ca cererea nu este reimprospatata (deferred) intArray[0] = 5; Console.WriteLine("---------"); // Afiseaza rezultatele din nou. foreach(int i in ints) Console.WriteLine(i);
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
Delegates de tip Func
Multi dintre operatorii standard de cereri au in prototipul lor un argument de tip delegate, numit Func.
Acest lucru ne scuteste de a declara in mod explicit un tip delegate.
public delegate TR Func<TR>(); public delegate TR Func<T0, TR>(T0 a0); public delegate TR Func<T0, T1, TR>(T0 a0, T1 a1); public delegate TR Func<T0, T1, T2, TR>(T0 a0, T1 a1, T2 a2); public delegate TR Func<T0, T1, T2, T3, TR>(T0 a0, T1 a1, T2 a2, T3 a3);
TR se refera la tipul de data returnat.
Exemplu
Prototipul pentru operatorul Where este (exista doua prototipuri):
public static IEnumerable<T> Where<T>( this IEnumerable<T> source, Func<T, bool> predicate);
Observam ca este metoda extinsa pentru IEnumerable<T>.
Argumentul predicat este specificat ca Func<T, bool>. De aici observam ca metoda predicate sau
lambda expresia accepta un singur argument, parametrul T si returneaza un bool. Tipul returnat este
ultimul in lista de parametri.
Ex // Cream un array de int int[] ints = new int[] { 1,2,3,4,5,6 }; // Declaram delegate Func<int, bool> MaiMareCaDoi = i => i > 2; // Declaram codul pentru cerere ... va fi “deferred” IEnumerable<int> result = ints.Where(MaiMareCaDoi); // Afisare rezultat foreach(int i in result)
Console.WriteLine(i); Rezultat: 3, 4, 5, 6
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
Table 3-1. Standard Query Operators Alphabetical Cross-Reference
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
Namespace-urile necesare sunt:
using System.Linq; using System.Collections; using System.Collections.Generic; using System.Data.Linq;
Operatori de restrictii
Where : filtreaza elementele dintr-un sir.
public static IEnumerable<T> Where<T>( this IEnumerable<T> source, Func<T, bool> predicate);
public static IEnumerable<T> Where<T>( this IEnumerable<T> source, Func<T, int, bool> predicate);
In: source – contine sirul de elemente;
predicate – metoda delegate folosita pentru filtrare.
int – reprezinta indexul elementului din sirul de intrare - source (zero based).
Out: obiecte pentru care predicate a returnat true.
Exceptii: ArgumentNullException – daca exista un argument null.
Ex string[] orase = {“Iasi”, “Vaslui”, “Botosani”, “Cluj”, “Timisoara”, “Bacau”}; // orasele ce incep cu litera I
IEnumerable<string> sir = orase.Where(o => o.StartsWith(“I”)); foreach(string s in sir)
Console.WriteLine(s); // se extrag din sir elementele plasate pe locuri impare IEnumerable<string> sirimpar = orase.Where((o,i) => (i &1) == 1);
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
foreach(string s in sirimpar) Console.WriteLine(s);
Ex. Din colectia carti se extrag acele elemente pentru care proprietatea Pret >= 10.
IEnumerable<Carti> x = carti.Where(p => p.Pret >= 10);
In C# 3.0 query expression arata astfel:
IEnumerable<Carti> x = from p in carti where p.Pret >= 10 select p;
carti – reprezinta colectia de Carti (vezi exemplul de cod). (SQL: Select * From carti p Where p.Pret >= 10).
Proiectie
Operatorii de proiectie returneaza un sir de elemente ce sunt generate prin selectarea elementelor sau
instantierea impreuna a noi elemente ce contin portiuni ale elementelor din sirul de intrare.
Tipul de data al elementelor din sirul de iesire poate fi diferit de tipul de data al elementelor din sirul
de intrare.
Select – folosit pentru a crea un sir de iesire de un anumit tip folosind in intrare un sir de un alt tip. public static IEnumerable<S> Select<T, S>( this IEnumerable<T> source, Func<T, S> selector);
public static IEnumerable<S> Select<T, S>( this IEnumerable<T> source, Func<T, int, S> selector);
In intrare avem tipul T, in iesire avem tipul S. Metoda delegate selector este folosita pentru a face
selectia.
Exceptii: ArgumentNullException – daca exista un argument null.
Exemplu:
IEnumerable<int> nrcar = orase.Select(o => o.Length) ; foreach(int i in nrcar)
Console.WriteLine({0}, i); var items = orase.Select((o, i) => new { oras = o, nc = o.Length} ); foreach(var v in items)
Console.WriteLine(“ Oras : {0} are {1} caractere in nume”, v.oras,v.nc);
SelectMany – creaza o proiectie de 1-n. Returneaza zero sau mai multe elemente pentru fiecare
element din intrare.
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
public static IEnumerable<S> SelectMany<T, S>( this IEnumerable<T> source, Func<T, IEnumerable<S>> selector);
public static IEnumerable<S> SelectMany<T, S>( this IEnumerable<T> source, Func<T, int, IEnumerable<S>> selector);
In intrare un element de tip T, in iesire un element de tip S, iar delegate este dat de selector.
Pentru fiecare element din sirul de intrare va rezulta un element concatenat in iesire.
Exceptii: ArgumentNullException – daca exista un argument null.
Folosind clasele Scriitori si Carti putem crea urmatoarea colectie :
// Colectiile de mai jos au in comun o data membru id Scriitori[] scriitori = Scriitori.GetScriitori(); Carti[] carti = Carti.GetCarti(); var items = scriitori .OrderBy(s => s.Nume) .SelectMany(e => carti .Where(eo => eo.id == e.id) .Select(eo => new
{ id = eo.id, Nume = e.Nume, Prenume = e.Prenume, titlu = eo.Titlu } ));
foreach (var item in items) Console.WriteLine(item);
(SQL: Select eo.id, e.Nume, e.Prenume, eo.Titlu From carti eo, scriitori e
Where eo.id = e.id
Order by e.Nume)
LINQ - Language Integrated Query 05.01.2014
Ioan Asiminoaei
Partitionare – acesti operatori ne permit sa returnam un sir ce este o submultime a sirului de intrare.
Take – returneaza un numar specificat de elemente din sirul de intrare plecand de la inceputul
sirului.
public static IEnumerable<T> Take<T>( this IEnumerable<T> source, int count);
In : count – specifica cate elemente trebuie sa luam din sirul de intrare, incepand cu primul element.
Ex : IEnumerable<string> items = orase.Take(5);
TakeWhile – returneaza elemente din sirul de intrare atata timp cat conditia este adevarata.
public static IEnumerable<T> TakeWhile<T>( this IEnumerable<T> source, Func<T, bool> predicate); public static IEnumerable<T> TakeWhile<T>( this IEnumerable<T> source, Func<T, int, bool> predicate);
Exceptii: ArgumentNullException
Skip – se vor sari atatea elemnete din sirul de intrare cate sunt specificate in count.
public static IEnumerable<T> Skip<T>( this IEnumerable<T> source, int count);
Exceptii: ArgumentNullException
SkipWhile – se vor sari elementele din intrare pana cand conditia devine false.
public static IEnumerable<T> SkipWhile<T>( this IEnumerable<T> source, Func<T, bool> predicate); public static IEnumerable<T> SkipWhile<T>( this IEnumerable<T> source, Func<T, int, bool> predicate);
Exceptii: ArgumentNullException
Concatenare – acesti operatori permit concatenarea sirurilor de acelasi tip.
Concat – concateneaza doua siruri de intrare si rezulta un singur sir.
public static IEnumerable<T> Concat<T>( this IEnumerable<T> first, IEnumerable<T> second);