Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Utilisez la join clause pour associer des éléments de différentes séquences sources qui n’ont aucune relation directe dans le modèle objet. La seule exigence est que les éléments de chaque source partagent une valeur que vous pouvez comparer pour l’égalité. Par exemple, un distributeur alimentaire peut avoir une liste de fournisseurs d’un certain produit et une liste d’acheteurs. Vous pouvez utiliser une join clause pour créer une liste des fournisseurs et acheteurs de ce produit qui se trouvent tous dans la même région spécifiée.
La documentation de référence du langage C# décrit la version la plus récente du langage C#. Il contient également la documentation initiale des fonctionnalités dans les préversions publiques pour la prochaine version du langage.
La documentation identifie toute fonctionnalité introduite en premier dans les trois dernières versions de la langue ou dans les préversions publiques actuelles.
Conseil / Astuce
Pour savoir quand une fonctionnalité a été introduite en C#, consultez l’article sur l’historique des versions du langage C#.
Une join clause prend deux séquences sources comme entrée. Les éléments de chaque séquence doivent être ou contenir une propriété que vous pouvez comparer à une propriété correspondante dans l’autre séquence. La join clause utilise le mot clé spécial equals pour comparer les clés spécifiées pour l’égalité. Toutes les jointures effectuées par la join clause sont des équijoins. La forme de la sortie d’une join clause dépend du type spécifique de jointure que vous effectuez. La liste suivante présente les trois types de jointure les plus courants :
- Jointure interne
- Rejoindre un groupe
- Jointure externe gauche
Jointure interne
L’exemple suivant montre un équijoin interne simple. Cette requête produit une séquence plate de paires « product name / category ». La même chaîne de catégorie apparaît dans plusieurs éléments. Si un élément n’a pas de categories correspondance products, cette catégorie n’apparaît pas dans les résultats.
var innerJoinQuery =
from category in categories
join prod in products on category.ID equals prod.CategoryID
select new { ProductName = prod.Name, Category = category.Name }; //produces flat sequence
Pour plus d’informations, consultez Effectuer des jointures internes.
Rejoindre un groupe
Une join clause avec une into expression est appelée jointure de groupe.
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 };
Une jointure de groupe produit une séquence de résultats hiérarchique, qui associe des éléments dans la séquence source de gauche à un ou plusieurs éléments correspondants dans la séquence source de droite. Une jointure de groupe n’a pas d’équivalent en termes relationnels ; il s’agit essentiellement d’une séquence de tableaux d’objets.
Si aucun élément de la séquence source de droite ne correspond à un élément dans la source gauche, la join clause produit un tableau vide pour cet élément. Par conséquent, la jointure groupée est fondamentalement une équijointure interne, excepté que la séquence de résultats est organisée en groupes.
Si vous sélectionnez simplement les résultats d’une jointure de groupe, vous pouvez accéder aux éléments, mais vous ne pouvez pas identifier la clé sur laquelle ils correspondent. Par conséquent, il est généralement plus utile de sélectionner les résultats de la jointure de groupe dans un nouveau type qui a également le nom de clé, comme indiqué dans l’exemple précédent.
Vous pouvez également utiliser le résultat d’une jointure de groupe comme générateur d’une autre sous-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;
Pour plus d’informations, consultez Effectuer des jointures groupées.
Jointure externe gauche
Dans une jointure externe gauche, la requête retourne tous les éléments de la séquence source de gauche, même si aucun élément correspondant n’est dans la séquence droite. Pour effectuer une jointure externe gauche dans LINQ, utilisez la DefaultIfEmpty méthode en combinaison avec une jointure de groupe pour spécifier un élément côté droit par défaut à produire si un élément de gauche n’a aucune correspondance. Vous pouvez utiliser null comme valeur par défaut pour n’importe quel type de référence, ou vous pouvez spécifier un type par défaut défini par l’utilisateur. Dans l’exemple suivant, un type par défaut défini par l’utilisateur est illustré :
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 };
Pour plus d’informations, consultez Effectuer des jointures externes gauches.
Opérateur Égal
Une clause join effectue une équijointure. En d’autres termes, vous pouvez baser les correspondances seulement sur l’égalité de deux clés. D’autres types de comparaisons tels que « supérieur à » ou « différent » ne sont pas pris en charge. Pour indiquer clairement que toutes les jointures sont des équijoins, la join clause utilise le equals mot clé au lieu de l’opérateur == . Le equals mot clé ne peut être utilisé que dans une join clause et il diffère de l’opérateur == de certaines manières importantes. Lors de la comparaison de chaînes, equals a une surcharge à comparer par valeur et l’opérateur == utilise l’égalité de référence. Lorsque les deux côtés de la comparaison ont des variables de chaîne identiques et equals== atteignent le même résultat : true. C’est parce que, lorsqu’un programme déclare deux variables de chaîne équivalentes ou plus, le compilateur les stocke tous dans le même emplacement. C’est ce qu’on appelle l’internement. La comparaison des valeurs Null constitue une autre différence importante : null equals null est évalué comme false avec l’opérateur equals, alors que l’opérateur == l’évalue comme true. Enfin, le comportement lié à l’étendue est différent : avec equals, la clé de gauche consomme la séquence source externe et la clé de droite consomme la source interne. La source externe est seulement dans l’étendue du côté gauche de equals et la séquence source interne est seulement dans l’étendue du côté droit.
Non-équijointures
Vous pouvez effectuer des jointures non équivalentes, des jointures croisées et d'autres opérations de jointure personnalisées en utilisant plusieurs clauses from pour introduire de nouvelles séquences indépendamment dans une requête. Pour plus d’informations, consultez Effectuer des opérations de jointure personnalisées.
Jointures sur des collections d’objets et sur des tables relationnelles
Dans une expression de requête LINQ, vous effectuez des opérations de jointure sur des collections d’objets. Vous ne pouvez pas joindre des collections d’objets de la même façon que deux tables relationnelles. Dans LINQ, vous n’avez besoin de clauses explicites join que lorsque deux séquences sources n’ont aucune relation. Lorsque vous utilisez LINQ to SQL, le modèle objet représente des tables de clés étrangères en tant que propriétés de la table primaire. Par exemple, dans la base de données Northwind, la table Customer a une relation de clé étrangère avec la table Orders. Lorsque vous mappez les tables au modèle objet, la classe Customer possède une Orders propriété qui contient la collection d’éléments Orders associés à ce client. En effet, la jointure est déjà effectuée pour vous.
Pour plus d’informations sur l’interrogation sur les tables associées dans le contexte de LINQ to SQL, consultez Guide pratique pour mapper les relations de base de données.
Clés composites
Vous pouvez tester l’égalité de plusieurs valeurs à l’aide d’une clé composite. Pour plus d’informations, consultez Effectuer des opérations de jointure à l’aide de clés composites. Vous pouvez également utiliser des clés composites dans une group clause.
Exemple :
L’exemple suivant compare les résultats d’une jointure interne, d’une jointure de groupe et d’une jointure externe gauche sur les mêmes sources de données à l’aide des mêmes clés correspondantes. Un code supplémentaire est ajouté à ces exemples pour clarifier les résultats dans l’affichage de la console.
class JoinDemonstration
{
#region Data
class Product
{
public required string Name { get; init; }
public required int CategoryID { get; init; }
}
class Category
{
public required string Name { get; init; }
public required int ID { get; init; }
}
// Specify the first data source.
List<Category> categories =
[
new Category {Name="Beverages", ID=001},
new Category {Name="Condiments", ID=002},
new Category {Name="Vegetables", ID=003},
new Category {Name="Grains", ID=004},
new Category {Name="Fruit", ID=005}
];
// Specify the second data source.
List<Product> products =
[
new Product {Name="Cola", CategoryID=001},
new Product {Name="Tea", CategoryID=001},
new Product {Name="Mustard", CategoryID=002},
new Product {Name="Pickles", CategoryID=002},
new Product {Name="Carrots", CategoryID=003},
new Product {Name="Bok Choy", CategoryID=003},
new Product {Name="Peaches", CategoryID=005},
new Product {Name="Melons", CategoryID=005},
];
#endregion
static void Main(string[] args)
{
JoinDemonstration app = new JoinDemonstration();
app.InnerJoin();
app.GroupJoin();
app.GroupInnerJoin();
app.GroupJoin3();
app.LeftOuterJoin();
app.LeftOuterJoin2();
}
void InnerJoin()
{
// Create the query that selects
// a property from each element.
var innerJoinQuery =
from category in categories
join prod in products on category.ID equals prod.CategoryID
select new { Category = category.ID, Product = prod.Name };
Console.WriteLine("InnerJoin:");
// Execute the query. Access results
// with a simple foreach statement.
foreach (var item in innerJoinQuery)
{
Console.WriteLine("{0,-10}{1}", item.Product, item.Category);
}
Console.WriteLine($"InnerJoin: {innerJoinQuery.Count()} items in 1 group.");
Console.WriteLine(System.Environment.NewLine);
}
void GroupJoin()
{
// This is a demonstration query to show the output
// of a "raw" group join. A more typical group join
// is shown in the GroupInnerJoin method.
var groupJoinQuery =
from category in categories
join prod in products on category.ID equals prod.CategoryID into prodGroup
select prodGroup;
// Store the count of total items (for demonstration only).
int totalItems = 0;
Console.WriteLine("Simple GroupJoin:");
// A nested foreach statement is required to access group items.
foreach (var prodGrouping in groupJoinQuery)
{
Console.WriteLine("Group:");
foreach (var item in prodGrouping)
{
totalItems++;
Console.WriteLine(" {0,-10}{1}", item.Name, item.CategoryID);
}
}
Console.WriteLine($"Unshaped GroupJoin: {totalItems} items in {groupJoinQuery.Count()} unnamed groups");
Console.WriteLine(System.Environment.NewLine);
}
void GroupInnerJoin()
{
var groupJoinQuery2 =
from category in categories
orderby category.ID
join prod in products on category.ID equals prod.CategoryID into prodGroup
select new
{
Category = category.Name,
Products = from prod2 in prodGroup
orderby prod2.Name
select prod2
};
//Console.WriteLine("GroupInnerJoin:");
int totalItems = 0;
Console.WriteLine("GroupInnerJoin:");
foreach (var productGroup in groupJoinQuery2)
{
Console.WriteLine(productGroup.Category);
foreach (var prodItem in productGroup.Products)
{
totalItems++;
Console.WriteLine(" {0,-10} {1}", prodItem.Name, prodItem.CategoryID);
}
}
Console.WriteLine($"GroupInnerJoin: {totalItems} items in {groupJoinQuery2.Count()} named groups");
Console.WriteLine(System.Environment.NewLine);
}
void GroupJoin3()
{
var groupJoinQuery3 =
from category in categories
join product in products on category.ID equals product.CategoryID into prodGroup
from prod in prodGroup
orderby prod.CategoryID
select new { Category = prod.CategoryID, ProductName = prod.Name };
//Console.WriteLine("GroupInnerJoin:");
int totalItems = 0;
Console.WriteLine("GroupJoin3:");
foreach (var item in groupJoinQuery3)
{
totalItems++;
Console.WriteLine($" {item.ProductName}:{item.Category}");
}
Console.WriteLine($"GroupJoin3: {totalItems} items in 1 group");
Console.WriteLine(System.Environment.NewLine);
}
void LeftOuterJoin()
{
// Create the query.
var leftOuterQuery =
from category in categories
join prod in products on category.ID equals prod.CategoryID into prodGroup
select prodGroup.DefaultIfEmpty(new Product() { Name = "Nothing!", CategoryID = category.ID });
// Store the count of total items (for demonstration only).
int totalItems = 0;
Console.WriteLine("Left Outer Join:");
// A nested foreach statement is required to access group items
foreach (var prodGrouping in leftOuterQuery)
{
Console.WriteLine("Group:");
foreach (var item in prodGrouping)
{
totalItems++;
Console.WriteLine(" {0,-10}{1}", item.Name, item.CategoryID);
}
}
Console.WriteLine($"LeftOuterJoin: {totalItems} items in {leftOuterQuery.Count()} groups");
Console.WriteLine(System.Environment.NewLine);
}
void LeftOuterJoin2()
{
// Create the query.
var leftOuterQuery2 =
from category in categories
join prod in products on category.ID equals prod.CategoryID into prodGroup
from item in prodGroup.DefaultIfEmpty()
select new { Name = item == null ? "Nothing!" : item.Name, CategoryID = category.ID };
Console.WriteLine($"LeftOuterJoin2: {leftOuterQuery2.Count()} items in 1 group");
// Store the count of total items
int totalItems = 0;
Console.WriteLine("Left Outer Join 2:");
// Groups have been flattened.
foreach (var item in leftOuterQuery2)
{
totalItems++;
Console.WriteLine("{0,-10}{1}", item.Name, item.CategoryID);
}
Console.WriteLine($"LeftOuterJoin2: {totalItems} items in 1 group");
}
}
/*Output:
InnerJoin:
Cola 1
Tea 1
Mustard 2
Pickles 2
Carrots 3
Bok Choy 3
Peaches 5
Melons 5
InnerJoin: 8 items in 1 group.
Unshaped GroupJoin:
Group:
Cola 1
Tea 1
Group:
Mustard 2
Pickles 2
Group:
Carrots 3
Bok Choy 3
Group:
Group:
Peaches 5
Melons 5
Unshaped GroupJoin: 8 items in 5 unnamed groups
GroupInnerJoin:
Beverages
Cola 1
Tea 1
Condiments
Mustard 2
Pickles 2
Vegetables
Bok Choy 3
Carrots 3
Grains
Fruit
Melons 5
Peaches 5
GroupInnerJoin: 8 items in 5 named groups
GroupJoin3:
Cola:1
Tea:1
Mustard:2
Pickles:2
Carrots:3
Bok Choy:3
Peaches:5
Melons:5
GroupJoin3: 8 items in 1 group
Left Outer Join:
Group:
Cola 1
Tea 1
Group:
Mustard 2
Pickles 2
Group:
Carrots 3
Bok Choy 3
Group:
Nothing! 4
Group:
Peaches 5
Melons 5
LeftOuterJoin: 9 items in 5 groups
LeftOuterJoin2: 9 items in 1 group
Left Outer Join 2:
Cola 1
Tea 1
Mustard 2
Pickles 2
Carrots 3
Bok Choy 3
Nothing! 4
Peaches 5
Melons 5
LeftOuterJoin2: 9 items in 1 group
Press any key to exit.
*/
Remarques
Clause join qui n’est pas suivie d’un intoJoin appel de méthode. Une join clause suivie d’un intoGroupJoin appel de méthode est traduite.
Voir aussi
- Mots clés de requête (LINQ)
- Language Integrated Query (LINQ)
- Opérations de jointure
- clause de groupe
- Effectuer des jointures externes gauches
- Effectuer des jointures internes
- Effectuer des jointures groupées
- Classer les résultats d’une clause de jointure
- Joindre à l’aide de clés composites
- Systèmes de base de données compatibles pour Visual Studio