Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Usare la join clausola per associare elementi di sequenze di origine diverse che non hanno alcuna relazione diretta nel modello a oggetti. L'unico requisito è che gli elementi di ogni origine condividono alcuni valori che è possibile confrontare per verificarne l'uguaglianza. Ad esempio, un distributore di alimenti potrebbe avere un elenco di fornitori di un determinato prodotto e un elenco di acquirenti. È possibile utilizzare una join clausola per creare un elenco dei fornitori e degli acquirenti del prodotto che si trovano tutti nella stessa area specificata.
Il riferimento al linguaggio C# documenta la versione rilasciata più di recente del linguaggio C#. Contiene anche la documentazione iniziale per le funzionalità nelle versioni di anteprima pubblica per la prossima versione del linguaggio di programmazione.
La documentazione identifica tutte le funzionalità introdotte nelle ultime tre versioni della lingua o nelle anteprime pubbliche correnti.
Suggerimento
Per trovare quando una funzionalità è stata introdotta per la prima volta in C#, vedere l'articolo sulla cronologia delle versioni del linguaggio C#.
Una clausola join accetta due sequenze di origine come input. Gli elementi in ogni sequenza devono essere o contenere una proprietà che è possibile confrontare con una proprietà corrispondente nell'altra sequenza. La join clausola usa la parola chiave speciale equals per confrontare le chiavi specificate per verificarne l'uguaglianza. Tutti i join usata dalla join clausola sono equijoins. La forma dell'output di una join clausola dipende dal tipo specifico di join che si sta eseguendo. L'elenco seguente mostra i tre tipi di join più comuni:
- Join interno
- Partecipazione al gruppo
- Join esterno sinistro
Join interno
L'esempio seguente mostra un semplice equijoin interno. Questa query produce una sequenza piatta di coppie "nome prodotto / categoria". La stessa stringa di categoria viene visualizzata in più elementi. Se un elemento da categories non ha corrispondenza products, tale categoria non viene visualizzata nei risultati.
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
Per ulteriori informazioni, vedere Eseguire inner join (unione interna).
Partecipazione al gruppo
Una clausola join con un'espressione into è chiamata join di gruppo.
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 };
Un join di gruppo produce una sequenza di risultati gerarchica, che associa gli elementi nella sequenza di origine sinistra a uno o più elementi corrispondenti nella sequenza di origine sul lato destro. Un join di gruppo non ha un equivalente in termini relazionali; è essenzialmente una sequenza di matrici di oggetti.
Se nessun elemento della sequenza di origine destra corrisponde a un elemento nell'origine sinistra, la join clausola produce una matrice vuota per tale elemento. Pertanto, il join di gruppo è ancora fondamentalmente un equijoin interno, tranne che la sequenza dei risultati è organizzata in gruppi.
Se si selezionano solo i risultati di un join di gruppo, è possibile accedere agli elementi, ma non è possibile identificare la chiave su cui corrispondono. Pertanto, è in genere più utile selezionare i risultati del join di gruppo in un nuovo tipo con anche il nome della chiave, come illustrato nell'esempio precedente.
È anche possibile usare il risultato di un join di gruppo come generatore di un'altra sottoquery:
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;
Per ulteriori informazioni, consultare Eseguire join raggruppati.
Join esterno sinistro
In un left outer join, la query restituisce tutti gli elementi nella sequenza di origine sinistra, anche se nella sequenza di destra non sono presenti elementi corrispondenti. Per eseguire un left outer join in LINQ, usare il metodo DefaultIfEmpty in combinazione con un join di gruppo per specificare un elemento predefinito sul lato destro da produrre se un elemento sul lato sinistro non ha corrispondenze. È possibile usare null come valore predefinito per qualsiasi tipo di riferimento oppure specificare un tipo predefinito definito dall'utente. Nell'esempio seguente viene visualizzato un tipo predefinito definito dall'utente:
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 };
Per ulteriori informazioni, vedere Esecuzione del left outer join.
Operatore di uguaglianza
Una clausola join esegue un equijoin. In altre parole, è possibile basare le corrispondenze solo sull'uguaglianza di due chiavi specifiche. Altri tipi di confronti, ad esempio "maggiore di" o "non uguale a" non sono supportati. Per chiarire che tutti i join sono equijoins, la clausola join usa la parola chiave equals anziché l'operatore ==. La parola chiave equals può essere usata solo in una clausola join e differisce dall'operatore == in alcuni modi importanti. Quando si confrontano le stringhe, equals ha un sovraccarico per il confronto basato sul valore e l'operatore == verifica l'uguaglianza dei riferimenti. Quando entrambi i lati del confronto hanno variabili stringa identiche, equals e == raggiungere lo stesso risultato: true. Questo perché, quando un programma dichiara due o più variabili di stringa equivalenti, il compilatore li archivia tutti nella stessa posizione. Questa operazione è nota come interning. Un'altra differenza importante è il confronto con i valori null: null equals null viene valutato come falso con l'operatore equals, anziché con l'operatore == che lo valuta come vero. Infine, il comportamento di ambito è diverso: con equals, il tasto sinistro utilizza la sequenza di origine esterna e il tasto destro utilizza la sequenza interna. L'origine esterna è solo nell'ambito a sinistra di equals e la sequenza di origine interna è solo nell'ambito a destra.
Non equijoins
È possibile eseguire operazioni non-equijoins, cross joins e altre operazioni di join personalizzate usando più clausole from per introdurre nuove sequenze in modo indipendente in una query. Per altre informazioni, vedere Eseguire operazioni di join personalizzate.
Join in raccolte di oggetti contro tabelle relazionali
In un'espressione di query LINQ si eseguono operazioni di join sulle raccolte di oggetti. Non è possibile unire raccolte di oggetti esattamente come due tabelle relazionali. In LINQ sono necessarie solo clausole esplicite join quando due sequenze di origine non hanno alcuna relazione. Quando si usa LINQ to SQL, il modello a oggetti rappresenta le tabelle chiave esterna come proprietà della tabella primaria. Nel database Northwind, ad esempio, la tabella Customer ha una relazione di chiave esterna con la tabella Orders. Quando si esegue il mapping delle tabelle al modello a oggetti, la classe Customer ha una Orders proprietà che contiene la raccolta di Orders associata a tale customer. In effetti, il join è già stato fatto per te.
Per altre informazioni sull'esecuzione di query tra tabelle correlate nel contesto di LINQ to SQL, vedere Procedura: Eseguire il mapping delle relazioni di database.
Chiavi composte
È possibile verificare l'uguaglianza di più valori usando una chiave composita. Per ulteriori informazioni, vedere Join utilizzando chiavi composite. È anche possibile usare chiavi composite in una group clausola .
Esempio
Nell'esempio seguente vengono confrontati i risultati di un 'inner join', un 'group join' e un 'left outer join' sugli stessi dati utilizzando le stesse chiavi di corrispondenza. Alcuni codici aggiuntivi vengono aggiunti a questi esempi per chiarire i risultati nella visualizzazione della 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.
*/
Osservazioni:
Una join clausola che non è seguita da into converte in una Join chiamata al metodo. Una join clausola seguita da into converte in una GroupJoin chiamata al metodo.
Vedere anche
- Parole chiave di query (LINQ)
- LINQ (Language-Integrated Query)
- Operazioni di Join
- Clausola di gruppo
- Eseguire join esterni sinistri
- Eseguire inner join
- Eseguire join raggruppati
- Ordinare i risultati di una clausola di giunzione
- Unisci usando chiavi composite
- Sistemi di database compatibili per Visual Studio