LINQ (Language-Integrated Query)
LINQ (Language-Integrated Query) è il nome di un set di tecnologie basate sull'integrazione delle funzionalità di query direttamente nel linguaggio C#. In genere, le query sui dati vengono espresse come stringhe semplici senza il controllo dei tipi in fase di compilazione o il supporto di IntelliSense. È anche necessario imparare un linguaggio di query diverso per ogni tipo di origine dati: database SQL, documenti XML, svariati servizi Web e così via. Con LINQ, una query è un costrutto del linguaggio di prima classe, come le classi, i metodi e gli eventi.
Quando si scrivono query, la parte integrata nel linguaggio più visibile di LINQ è l'espressione di query. Le espressioni di query vengono scritte con una sintassi di query dichiarativa. Tramite la sintassi di query è possibile eseguire operazioni di filtro, ordinamento e raggruppamento sulle origini dati usando una quantità minima di codice. Si usano gli stessi modelli di espressione di query per eseguire query e trasformare i dati da qualsiasi tipo di origine dati.
L'esempio seguente mostra un’operazione di query completa. L'operazione completa include la creazione di un'origine dati, la definizione dell'espressione di query e l'esecuzione della query in un'istruzione foreach
.
// Specify the data source.
int[] scores = [97, 92, 81, 60];
// Define the query expression.
IEnumerable<int> scoreQuery =
from score in scores
where score > 80
select score;
// Execute the query.
foreach (var i in scoreQuery)
{
Console.Write(i + " ");
}
// Output: 97 92 81
Potrebbe essere necessario aggiungere una direttiva using
, using System.Linq;
, per l'esempio precedente per la compilazione. Le versioni più recenti di .NET usano usi impliciti per aggiungere questa direttiva come uso globale. Le versioni precedenti richiedono di aggiungerlo nell'origine.
Panoramica sulle espressioni di query
- Le espressioni di query eseguono query su e trasformano i dati da qualsiasi origine dati abilitata per LINQ. Una sola query, ad esempio, è in grado di recuperare dati da un database SQL e di produrre un flusso XML come output.
- Le espressioni di query usano molti costrutti di linguaggio C# familiari, che semplificano la lettura.
- Le variabili in un'espressione di query sono tutte fortemente tipate.
- Una query non viene eseguita finché non si esegue l'iterazione della variabile di query, ad esempio in un'istruzione
foreach
. - In fase di compilazione, le espressioni di query vengono convertite in chiamate al metodo dell'operatore query standard in base alle regole definite nella specifica C#. Le query che possono essere espresse usando la sintassi di query possono essere espresse anche usando la sintassi dei metodi. In alcuni casi, la sintassi delle query è più leggibile e concisa. In altri casi, la sintassi del metodo è più leggibile. Non esiste alcuna differenza semantica o di prestazioni tra le due forme diverse. Per altre informazioni, vedere Specifiche del linguaggio C# e Panoramica degli operatori di query standard.
- Per alcune operazioni di query, ad esempio Count o Max, non è presente una clausola dell'espressione di query equivalente. Tali espressioni devono quindi essere espresse come chiamata di metodo. La sintassi dei metodi può essere combinata con la sintassi di query in diversi modi.
- Le espressioni di query possono essere compilate in alberi delle espressioni o in delegati, a seconda del tipo al quale viene applicata la query. Le query IEnumerable<T> vengono compilate in delegati. Le query IQueryable e IQueryable<T> vengono compilate in alberi delle espressioni. Per altre informazioni, vedere Alberi delle espressioni.
Come attivare l'esecuzione di query LINQ sull'origine dati
Dati in memoria
Ci sono due modi per abilitare l'esecuzione di query LINQ sui dati in memoria. Se i dati sono di un tipo che implementa IEnumerable<T>, è possibile eseguire una query sui dati usando LINQ to Objects. Se non ha senso abilitare l'enumerazione implementando l'interfaccia IEnumerable<T>, definire i metodi dell'operatore di query standard LINQ, in tale tipo o come metodi di estensione per tale tipo. Le implementazioni personalizzate degli operatori di query standard devono utilizzare l'esecuzione posticipata per restituire i risultati.
Dati remoti
L'opzione migliore per abilitare l'esecuzione di query LINQ su un'origine dati remota consiste nell'implementare l'interfaccia IQueryable<T>.
Provider LINQ IQueryable
I provider LINQ che implementano IQueryable<T> possono variare notevolmente per quanto riguarda la complessità.
Un provider IQueryable
meno complesso potrebbe accedere a un singolo metodo da un servizio Web. Questo tipo di provider è molto specifico poiché prevede informazioni specifiche nelle query che gestisce. Ha un sistema del tipo chiuso, forse esponendo un solo tipo di risultato. La maggior parte dell'esecuzione della query avviene localmente, utilizzando ad esempio le implementazioni Enumerable degli operatori di query standard. Un provider meno complesso potrebbe esaminare solo un'espressione della chiamata al metodo nella struttura ad albero dell'espressione che rappresenta la query facendo sì che la logica rimanente della query venga gestita altrove.
Un provider IQueryable
mediamente complesso potrebbe essere destinato a un'origine dati che ha un linguaggio di query parzialmente espressivo. Se è destinato a un servizio Web, potrebbe accedere a più metodi del servizio Web e selezionare il metodo da chiamare in base alle informazioni ricercate dalla query. Un provider mediamente complesso può avere un sistema di tipi più dettagliato rispetto a un provider semplice, ma rimane sempre un sistema di tipi fisso. Ad esempio, il provider può esporre tipi che hanno relazioni uno-a-molti che possono essere attraversate, ma non fornisce la tecnologia di mapping per i tipi definiti dall'utente.
Un provider IQueryable
complesso, ad esempio il provider Entity Framework Core, potrebbe convertire query LINQ complete in un linguaggio di query espressivo, ad esempio SQL. Un provider complesso è più generale perché può gestire un'ampia gamma di domande nella query. Ha anche un sistema di tipi aperto e pertanto deve contenere un'infrastruttura completa per eseguire il mapping dei tipi definiti dall'utente. Lo sviluppo di un provider complesso è molto impegnativo.