Démarrer avec linq
Installer
LINQ nécessite .NET 3.5 ou supérieur (ou .NET 2.0 en utilisant LINQBridge).
Ajoutez une référence à System.Core, si elle n’a pas encore été ajoutée.
En haut du fichier, importez l’espace de noms :
*C#
using System;
using System.Linq;
- VB.NET
Imports System.Linq
Les différentes jointures dans LINQ
Dans les exemples suivants, nous utiliserons les exemples suivants :
List<Product> Products = new List<Product>()
{
new Product()
{
ProductId = 1,
Name = "Book nr 1",
Price = 25
},
new Product()
{
ProductId = 2,
Name = "Book nr 2",
Price = 15
},
new Product()
{
ProductId = 3,
Name = "Book nr 3",
Price = 20
},
};
List<Order> Orders = new List<Order>()
{
new Order()
{
OrderId = 1,
ProductId = 1,
},
new Order()
{
OrderId = 2,
ProductId = 1,
},
new Order()
{
OrderId = 3,
ProductId = 2,
},
new Order()
{
OrderId = 4,
ProductId = NULL,
},
};
JOINTURE INTERNE
Syntaxe de la requête
var joined = (from p in Products
join o in Orders on p.ProductId equals o.ProductId
select new
{
o.OrderId,
p.ProductId,
p.Name
}).ToList();
Syntaxe de la méthode
var joined = Products.Join(Orders, p => p.ProductId,
o => o.OrderId,
=> new
{
OrderId = o.OrderId,
ProductId = p.ProductId,
Name = p.Name
})
.ToList();
Résultat:
{ 1, 1, "Book nr 1" },
{ 2, 1, "Book nr 1" },
{ 3, 2, "Book nr 2" }
JOINTURE EXTERNE GAUCHE
var joined = (from p in Products
join o in Orders on p.ProductId equals o.ProductId into g
from lj in g.DefaultIfEmpty()
select new
{
//For the empty records in lj, OrderId would be NULL
OrderId = (int?)lj.OrderId,
p.ProductId,
p.Name
}).ToList();
Résultat:
{ 1, 1, "Book nr 1" },
{ 2, 1, "Book nr 1" },
{ 3, 2, "Book nr 2" },
{ NULL, 3, "Book nr 3" }
JOINTURE CROISÉE
var joined = (from p in Products
from o in Orders
select new
{
o.OrderId,
p.ProductId,
p.Name
}).ToList();
Résultat:
{ 1, 1, "Book nr 1" },
{ 2, 1, "Book nr 1" },
{ 3, 2, "Book nr 2" },
{ NULL, 3, "Book nr 3" },
{ 4, NULL, NULL }
** REJOINDRE LE GROUPE **
var joined = (from p in Products
join o in Orders on p.ProductId equals o.ProductId
into t
select new
{
p.ProductId,
p.Name,
Orders = t
}).ToList();
La propriété Orders
contient maintenant un IEnumerable<Order>
avec toutes les commandes liées.
Résultat:
{ 1, "Book nr 1", Orders = { 1, 2 } },
{ 2, "Book nr 2", Orders = { 3 } },
{ 3, "Book nr 3", Orders = { } },
Comment s’inscrire sous plusieurs conditions
Lorsque vous vous joignez à une seule condition, vous pouvez utiliser :
join o in Orders
on p.ProductId equals o.ProductId
Lorsque vous vous joignez à plusieurs, utilisez :
join o in Orders
on new { p.ProductId, p.CategoryId } equals new { o.ProductId, o.CategoryId }
Assurez-vous que les deux objets anonymes ont les mêmes propriétés, et dans VB.NET, ils doivent être marqués Key
, bien que VB.NET autorise plusieurs clauses Equals
séparées par And
:
Join o In Orders
On p.ProductId Equals o.ProductId And p.CategoryId Equals o.CategoryId
Syntaxe de requête et syntaxe de méthode
La syntaxe de requête et la syntaxe de méthode sont sémantiquement identiques, mais de nombreuses personnes trouvent la syntaxe de requête plus simple et plus facile à lire. Disons que nous devons récupérer tous les éléments pairs classés par ordre croissant à partir d’une collection de nombres.
C# :
int[] numbers = { 0, 1, 2, 3, 4, 5, 6 };
// Query syntax:
IEnumerable<int> numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;
// Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);
VB.NET :
Dim numbers() As Integer = { 0, 1, 2, 3, 4, 5, 6 }
' Query syntax: '
Dim numQuery1 = From num In numbers
Where num Mod 2 = 0
Select num
Order By num
' Method syntax: '
Dim numQuery2 = numbers.where(Function(num) num Mod 2 = 0).OrderBy(Function(num) num)
N’oubliez pas que certaines requêtes doivent être exprimées sous forme d’appels de méthode. Par exemple, vous devez utiliser un appel de méthode pour exprimer une requête qui récupère le nombre d’éléments qui correspondent à une condition spécifiée. Vous devez également utiliser un appel de méthode pour une requête qui récupère l’élément qui a la valeur maximale dans une séquence source. Cela pourrait donc être un avantage d’utiliser la syntaxe de méthode pour rendre le code plus cohérent. Cependant, bien sûr, vous pouvez toujours appliquer la méthode après un appel de syntaxe de requête :
C# :
int maxNum =
(from num in numbers
where num % 2 == 0
select num).Max();
VB.NET :
Dim maxNum =
(From num In numbers
Where num Mod 2 = 0
Select num).Max();
Méthodes LINQ et IEnumerable vs IQueryable
Les méthodes d’extension LINQ sur IEnumerable<T>
prennent les méthodes réelles1, que les méthodes anonymes :
//C#
Func<int,bool> fn = x => x > 3;
var list = new List<int>() {1,2,3,4,5,6};
var query = list.Where(fn);
'VB.NET
Dim fn = Function(x As Integer) x > 3
Dim list = New List From {1,2,3,4,5,6};
Dim query = list.Where(fn);
ou des méthodes nommées (méthodes explicitement définies comme faisant partie d’une classe) :
//C#
class Program {
bool LessThan4(int x) {
return x < 4;
}
void Main() {
var list = new List<int>() {1,2,3,4,5,6};
var query = list.Where(LessThan4);
}
}
'VB.NET
Class Program
Function LessThan4(x As Integer) As Boolean
Return x < 4
End Function
Sub Main
Dim list = New List From {1,2,3,4,5,6};
Dim query = list.Where(AddressOf LessThan4)
End Sub
End Class
En théorie, il est possible d’analyser l’IL de la méthode, de comprendre ce que la méthode essaie de faire et d’appliquer la logique de cette méthode à n’importe quelle source de données sous-jacente, pas seulement objets en mémoire. Mais l’analyse IL n’est pas pour les faibles de cœur.
Heureusement, .NET fournit l’interface IQueryable<T>
et les méthodes d’extension à System.Linq.Queryable
, pour ce scénario. Ces méthodes d’extension prennent un arbre d’expression — une structure de données représentant le code — au lieu d’une méthode réelle, que le fournisseur LINQ peut ensuite analyser2 et convertir en une forme plus appropriée pour interroger la source de données sous-jacente. Par exemple:
//C#
IQueryable<Person> qry = PersonsSet();
// Since we're using a variable of type Expression<Func<Person,bool>>, the compiler
// generates an expression tree representing this code
Expression<Func<Person,bool>> expr = x => x.LastName.StartsWith("A");
// The same thing happens when we write the lambda expression directly in the call to
// Queryable.Where
qry = qry.Where(expr);
'VB.NET
Dim qry As IQueryable(Of Person) = PersonSet()
' Since we're using a variable of type Expression(Of Func(Of Person,Boolean)), the compiler
' generates an expression tree representing this code
Dim expr As Expression(Of Func(Of Person, Boolean)) = Function(x) x.LastName.StartsWith("A")
' The same thing happens when we write the lambda expression directly in the call to
' Queryable.Where
qry = qry.Where(expr)
Si (par exemple) cette requête porte sur une base de données SQL, le fournisseur peut convertir cette expression en l’instruction SQL suivante :
SELECT *
FROM Persons
WHERE LastName LIKE N'A%'
et exécutez-le sur la source de données.
D’un autre côté, si la requête porte sur une API REST, le fournisseur peut convertir la même expression en un appel d’API :
http://www.example.com/person?filtervalue=A&filtertype=startswith&fieldname=lastname
Il existe deux principaux avantages à personnaliser une demande de données basée sur une expression (par opposition au chargement de l’intégralité de la collection en mémoire et à l’interrogation locale) :
- La source de données sous-jacente peut souvent interroger plus efficacement. Par exemple, il peut très bien y avoir un index sur
LastName
. Charger les objets dans la mémoire locale et interroger en mémoire perd cette efficacité. - Les données peuvent être mises en forme et réduites avant d’être transférées. Dans ce cas, la base de données / le service Web n’a besoin de renvoyer que les données correspondantes, par opposition à l’ensemble complet des personnes disponibles à partir de la source de données.
Remarques
ToString
en SQL équivalent.