Linq en 15 min

Pour qu’on puisse utiliser une source de données avec Linq, il faut que la liste prenne en charge IEnumerable ou IQueryable (IEnumerable se suffit pas, il faut IEnumerable). Linq permet d’effectuer des requêtes sur des types très différents de données. Au moyen de “providers”, on peut effectuer des requêtes sur des fichiers XML avec “Linq to XML”; faire des requêtes sur un SQL server avec “Linq to SQL” ou faire des requêtes sur des entités avec “Linq to Entities”.

Syntaxe

On va s’intéresser à la syntaxe générale de Linq pour les requêtes sur des IEnumerable. Pour que Linq soit utilisable, il faut que la structure de données prenne en charge IEnumerable, si elle prends en charge seulement IEnumerable, va ne sera pas suffisant. La syntaxe générale est:

IEnumerable result = 
       from [identifiant local] in [structure de données] 
       where [condition sur une propriété de l'identifiant] 
       select [propriété de l'identifiant à retourner]

Le résultat des requêtes Linq peuvent être: – IEnumerable, – iEnumerable ou – iQueryable

Exécution

L’exécution des requêtes n’est pas immédiate. Elle sera effectué lorsque la structure du résultat sera utilisée dans une boucle Foreach. Pour forcer l’exécution de la requête, on peut utiliser des fonctions d’agrégation: Count(), Max(), Min() ou Average(). La syntaxe pour ces fonctions est:

var evenNumQuery = 
    from num in numbers 
    where (num % 2) == 0 
    select num; 
int evenNumCount = evenNumQuery.Count();

Si on doit retourner une liste, il faut utiliser: ToList ou ToArray. La syntaxe est alors:

List numQuery2 = 
    (from num in numbers 
     where (num % 2) == 0 
     select num).ToList(); 
// or like this: 
// numQuery3 is still an int[] 
var numQuery3 = 
    (from num in numbers 
     where (num % 2) == 0 
     select num).ToArray();

Clause SELECT

Quelques exemples de clause SELECT: Méthode d’extension Linq exécutée sur une liste:

ContactInfo cInfo = 
     (from ci in app.contactList 
     where ci.ID == id 
     select ci) 
     .FirstOrDefault();

Retour d’un élément de la liste directement:

IEnumerable studentQuery1 = 
     from student in app.students 
     where student.ID > 111 
     select student;

Retour d’un membre d’un élément de la liste:

IEnumerable studentQuery2 = 
     from student in app.students 
     where student.ID > 111 
     select student.Last; 
 
IEnumerable studentQuery4 = 
     from student in app.students 
     where student.ID > 111 
     select student.Scores[0]; 

IEnumerable studentQuery5 = 
     from student in app.students 
     where student.ID > 111 
     select student.Scores[0] * 1.1;

Exécution d’une fonction dans l’élément retourné:

IEnumerable studentQuery3 = 
     from student in app.students 
     where student.ID > 111 
     select student.GetContactInfo(app, student.ID);
 
IEnumerable studentQuery6 = 
     from student in app.students 
     where student.ID > 111 
     select student.Scores.Average();

Retour d’un nouvel élément avec un initialiseur d’objet:

var studentQuery7 = 
     from student in app.students 
     where student.ID > 111 
     select new { student.First, student.Last };
 
IEnumerable studentQuery8 = 
     from student in app.students 
     where student.ID > 111 
     select new ScoreInfo 
     { 
         Average = student.Scores.Average(), 
         ID = student.ID 
     };

Utilisation d’une jointure:

IEnumerable studentQuery9 = 
     from student in app.students 
     where student.Scores.Average() > 85 
     join ci in app.contactList on student.ID equals ci.ID 
     select ci;

Lien MSDN

Clause FROM

Il est possible d’utiliser des “From” imbriqués qui correspondent à 2 boucles “foreach”: une première boucle pour la liste d’élément et une 2ème pour une liste dans la première liste:

var scoreQuery = 
     from student in students 
     from score in student.Scores 
     where score > 90 
     select new { Last = student.LastName, score };

Le “select” permet de retourner des éléments parmi ceux manipulés dans les 2 from. On peut faire des clauses from imbriquées pour des listes qui n’ont pas de lien entres elles:

char[] upperCase = { 'A', 'B', 'C' }; 
char[] lowerCase = { 'x', 'y', 'z' }; 
 
var joinQuery1 = 
     from upper in upperCase 
     from lower in lowerCase 
     select new { upper, lower }; 
 
var joinQuery2 = 
     from lower in lowerCase 
     where lower != 'x' 
     from upper in upperCase 
     select new { lower, upper };

Clause WHERE

Opérateurs logiques

Dans une clause WHERE, on peut spécifier plusieurs conditions séparées par des opérateurs logiques ET et OU mais ATTENTION il faut utiliser: – “&&” pour ET – “||” pour OU Par exemple:

var queryLondonCustomers =  
     from cust in customers  
     where cust.City=="London" && cust.Name == "Devon" 
     select cust;

Utilisation de fonctions

On peut utiliser une fonction dans la clause WHERE:

var queryEvenNums =  
     from num in numbers  
     where IsEven(num)  
     select num;

Lien MSDN

Clause JOIN

Opérateur de comparaison “EQUALS”

L’opérateur de comparaison utilisé pour les jointures est seulement “EQUALS”. On ne peut pas utiliser “supérieur à” ou “différent de” ce qui limite beaucoup fonctionnellement l’intérêt de linq.

Jointure interne

Seuls les éléments communs aux 2 listes sont retournés:

var innerJoinQuery = 
     from category in categories 
     join prod in products on category.ID equals prod.CategoryID 
     select new { ProductName = prod.Name, Category = category.Name };

Jointure avec une clé composite:

On peut appliquer l’opérateur d’égalité sur une structure plus complexe:

IEnumerable query =  
     from employee in employees  
     join student in students on new { employee.FirstName, employee.LastName } 
     equals new { student.FirstName, student.LastName }  
     select employee.FirstName + " " + employee.LastName;

Jointure multiple

Comme pour les autres opérateurs, on peut utiliser plusieurs clause JOIN pour effectuer une jointure multiple:

var query =  
     from person in people  
     join cat in cats on person equals cat.Owner  
     join dog in dogs on new { Owner = person, Letter = cat.Name.Substring(0, 1)} 
     equals new { dog.Owner, Letter = dog.Name.Substring(0, 1) }  
     select new { CatName = cat.Name, DogName = dog.Name };

Jointure groupée

Ce type de jointure n’a pas d’équivalent en requête relationnelle, elle permet d’identifier le résultat d’une jointure dans une variable pour être facilement utilisable dans la clause SÉLECT ou dans une nouvelle requête:

var innerGroupJoinQuery = 
    from category in categories 
    join prod in products on category.ID equals prod.CategoryID into prodGroup 
    select new { CategoryName = category.Name, Products = prodGroup };

Pour une nouvelle requête:

var innerGroupJoinQuery2 = 
    from category in categories 
    join prod in products on category.ID equals prod.CategoryID into prodGroup 
    from prod2 in prodGroup 
    where prod2.UnitPrice > 2.50M 
    select prod2;

Jointure externe gauche

On retourne les éléments de la source de gauche même s’ils n’existent pas dans la source de droite. Il faut utiliser la méthode “DefaultIfEmpty” avec une jointure groupée pour indiquer les valeurs à retourner si la source de droite ne contient pas de valeurs.

var leftOuterJoinQuery = 
    from category in categories 
    join prod in products on category.ID equals prod.CategoryID into prodGroup 
    from item in prodGroup.DefaultIfEmpty(new Product { Name = String.Empty, CategoryID = 0 }) 
    select new { CatName = category.Name, ProdName = item.Name };

Lien MSDN

Non equi-jointure

Une jointure n’est possible qu’avec un opérateur d’égalité. Pour utiliser un autre opérateur, il existe quelques astuces en utilisant 2 clauses FROM:

var nonEquijoinQuery = 
     from p in products 
     let catIds = from c in categories 
               select c.ID 
     where catIds.Contains(p.CategoryID) == true 
     select new { Product = p.Name, CategoryID = p.CategoryID };

Lien MSDN

Mot clé ORDERBY

Permet d’ordonner les résultats dans la structure IEnumerable. On utilise: – “ascending” pour l’ordre croissant, – “descending” pour l’ordre décroissant.

IEnumerable sortDescendingQuery =  
     from w in fruits  
     orderby w descending  
     select w;

On peut ordonner suivant plusieurs éléments:

IEnumerable sortedStudents =  
from student in students  
orderby student.Last ascending, student.First ascending  
select student;

Lien MSDN

Clause GROUP…BY

“Group…by” permet de grouper les éléments mais attention la structure de sortie n’est plus une IEnumerable mais IEnumerable<IGrouping>. TGROUPING est le type ayant permis de grouper les éléments. IGrouping est comme un dictionnaire. ATTENTION: ce mot clé s’utilise avec la syntaxe:

GROUP [identifiant local] BY [élément utilisé pour grouper]

Il n’est pas nécessaire d’utiliser une clause SELECT lorsqu’on utilise le mot clé GROUP…BY:

var queryCustomersByCity =  
     from cust in customers  
     group cust by cust.City;

Utilisation avec INTO:

Into permet d’utiliser une variable qui sera utilisée ensuite dans une autre clause WHERE ou SELECT:

var custQuery =  
     from cust in customers  
     group cust by cust.City into custGroup  
     where custGroup.Count() > 2  
     orderby custGroup.Key  
     select custGroup;

Lien MSDN

Mot clé LET

LET permet de définir une variable qui pourra être utilisée dans une 2e clause FROM, dans une clause SELECT ou dans une clause WHERE:

var earlyBirdQuery =  
     from sentence in strings 
     let words = sentence.Split(' ') 
     from word in words 
     let w = word.ToLower() 
     where w[0] == 'a' || w[0] == 'e' || w[0] == 'i' || w[0] == 'o' || w[0] == 'u' 
     select word;

Lien MSDN

Leave a Reply