Scrittura della prima query LINQ (Visual Basic)

Una query è un'espressione che recupera dati da un'origine dati. Le query sono espresse in un linguaggio di query dedicato. Nel tempo sono stati sviluppati diversi linguaggi per i vari tipi di origini dati, ad esempio SQL per i database relazionali e XQuery per XML. Gli sviluppatori devono quindi imparare un nuovo linguaggio di query per ogni tipo di origine dati o formato dati supportato.

LINQ (Language-Integrated Query) semplifica la questione offrendo un modello coerente per l'uso dei dati con tutti i diversi tipi di origini e formati dati. In una query LINQ si lavora sempre con gli oggetti. Vengono usati gli stessi criteri di codifica di base per eseguire una query e trasformare i dati in documenti XML, database SQL, set di dati ed entità ADO.NET, raccolte .NET Framework e qualsiasi altra origine o formato per cui sia disponibile un provider LINQ. Questo documento descrive le tre fasi della creazione e dell'uso di query LINQ di base.

Le tre fasi di un'operazione di query

Le operazioni di query LINQ sono costituite da tre azioni:

  1. Ottenere una o più origini dati.

  2. Creare la query.

  3. Eseguire la query.

In LINQ l'esecuzione di una query è un'operazione distinta dalla creazione della query. Non è sufficiente creare una query per recuperare i dati. Questo punto viene illustrato più dettagliatamente di seguito in questo argomento.

Nell'esempio seguente vengono illustrate le tre parti di un'operazione di query. Nell'esempio viene usata una matrice di interi come origine dati utile a scopo dimostrativo. Tuttavia, gli stessi concetti sono validi anche per altre origini dati.

Nota

Nella pagina Compilazione, Creazione progetti (Visual Basic) assicurarsi che Option Infer sia impostato su On.

' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}

' Query creation.
Dim evensQuery = From num In numbers
                 Where num Mod 2 = 0
                 Select num

' Query execution.
For Each number In evensQuery
    Console.Write(number & " ")
Next

Output:

0 2 4 6

Origine dati

Poiché l'origine dati nell'esempio precedente è una matrice, viene supportata implicitamente l'interfaccia generica IEnumerable<T>. È questo che consente di usare un array come origine dati per una query LINQ. I tipi che supportano IEnumerable<T> o un'interfaccia derivata, ad esempio l'interfaccia generica IQueryable<T> sono denominati tipi queryable.

In quanto tipo queryable in modo implicito, la matrice non richiede alcuna modifica o trattamento speciale per essere usata come origine dati LINQ. Lo stesso vale per qualsiasi tipo di raccolta che supporta l'interfaccia IEnumerable<T>, incluse le classi generiche List<T>, Dictionary<TKey,TValue> e altre classi nella libreria di classi .NET Framework.

Se i dati di origine non implementano già IEnumerable<T>, è necessario un provider LINQ per implementare la funzionalità degli operatori di query standard per tale origine dati. Ad esempio, LINQ to XML gestisce le operazioni di caricamento di un documento XML in un tipo queryable XElement, come illustrato nell'esempio seguente. Per altre informazioni sugli operatori di query standard, vedere Panoramica degli operatori di query standard (Visual Basic).

' Create a data source from an XML document.
Dim contacts = XElement.Load("c:\myContactList.xml")

Con LINQ to SQL è necessario creare prima un mapping relazionale a oggetti in fase di progettazione, manualmente o usando gli strumenti LINQ to SQL in Visual Studio. È possibile scrivere le query sugli oggetti e in fase di esecuzione LINQ to SQL gestisce la comunicazione con il database. Nell'esempio seguente customers rappresenta una tabella specifica nel database e Table<TEntity> supporta l'interfaccia generica IQueryable<T>.

' Create a data source from a SQL table.
Dim db As New DataContext("C:\Northwind\Northwnd.mdf")
Dim customers As Table(Of Customer) = db.GetTable(Of Customer)

Per altre informazioni sulla creazione di tipi specifici di origini dati, vedere la documentazione dei diversi provider LINQ. Per un elenco di questi provider, vedere LINQ (Language-Integrated Query). La regola di base è semplice: un'origine dati LINQ è qualsiasi oggetto che supporti l'interfaccia generica IEnumerable<T> o un'interfaccia da essa ereditata.

Nota

È anche possibile usare come origini dati LINQ tipi come ArrayList che supportano l'interfaccia non generica IEnumerable. Per un esempio in cui viene usato un tipo ArrayList, vedere Procedura: Eseguire una query su un ArrayList con LINQ (Visual Basic).

La query

Nella query è necessario specificare le informazioni da recuperare dalle origini dati. È anche possibile scegliere di specificare come ordinare, raggruppare e strutturare le informazioni prima che vengano restituite. Per abilitare la creazione di query, Visual Basic ha incorporato la nuova sintassi di query nel linguaggio.

Quando viene eseguita, la query nell'esempio seguente restituisce tutti i numeri pari di una matrice di interi numbers.

' Data source.
Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6}

' Query creation.
Dim evensQuery = From num In numbers
                 Where num Mod 2 = 0
                 Select num

' Query execution.
For Each number In evensQuery
    Console.Write(number & " ")
Next

L'espressione di query contiene tre clausole: From, Where e Select. La funzione e lo scopo specifici di ogni clausola dell'espressione di query sono descritti in Operazioni di query di base (Visual Basic). Per altre informazioni, vedere Query. Si noti che in LINQ una definizione di query viene spesso archiviata in una variabile ed eseguita in un secondo momento. La variabile di query, ad esempio evensQuery nell'esempio precedente, deve essere un tipo queryable. Il tipo di evensQuery è IEnumerable(Of Integer), assegnato dal compilatore usando l'inferenza del tipo di variabile locale.

È importante ricordare che la variabile di query stessa non esegue alcuna azione e non restituisce dati, ma si limita ad archiviare la definizione di query. Nell'esempio precedente si tratta del ciclo For Each che esegue la query.

Esecuzione di query

L'esecuzione di query è un'operazione distinta da quella di creazione di query. La query viene definita durante la creazione, ma l'esecuzione viene attivata da un meccanismo diverso. Una query può essere eseguita non appena viene definita (esecuzione immediata); in alternativa, è possibile archiviare la definizione ed eseguire la query in un secondo momento (esecuzione posticipata).

Esecuzione posticipata

Una query LINQ tipica è simile a quella dell'esempio precedente, in cui viene definita la variabile evensQuery. Crea la query ma non la esegue immediatamente. La definizione della query viene invece archiviata nella variabile di query evensQuery. La query viene eseguita in un secondo momento, in genere usando un ciclo For Each, che restituisce una sequenza di valori, oppure applicando un operatore query standard, come Count o Max. Questo processo è noto come esecuzione posticipata.

' Query execution that results in a sequence of values.
For Each number In evensQuery
    Console.Write(number & " ")
Next

' Query execution that results in a single value.
Dim evens = evensQuery.Count()

Per una sequenza di valori, si accede ai dati recuperati usando la variabile di iterazione nel ciclo For Each (number nell'esempio precedente). Dal momento che la variabile di query evensQuery contiene la definizione e non i risultati della query, è possibile eseguire una query il numero di volte desiderato usando più volte la variabile di query. Ad esempio, è possibile avere nell'applicazione un database che viene aggiornato continuamente mediante un'applicazione separata. Dopo aver creato una query che recupera i dati da tale database, è possibile usare un ciclo For Each per eseguire ripetutamente la query, recuperando i dati più recenti ogni volta.

Nell'esempio seguente viene illustrato il funzionamento dell'esecuzione posticipata. Dopo aver definito ed eseguito evensQuery2 con un ciclo For Each, come negli esempi precedenti, alcuni elementi nell'origine dati numbers vengono modificati. Un secondo ciclo For Each esegue quindi di nuovo evensQuery2. La seconda volta i risultati sono diversi, perché il ciclo For Each esegue di nuovo la query, usando i nuovi valori in numbers.

Dim numberArray() = {0, 1, 2, 3, 4, 5, 6}

Dim evensQuery2 = From num In numberArray
                  Where num Mod 2 = 0
                  Select num

Console.WriteLine("Evens in original array:")
For Each number In evensQuery2
    Console.Write("  " & number)
Next
Console.WriteLine()

' Change a few array elements.
numberArray(1) = 10
numberArray(4) = 22
numberArray(6) = 8

' Run the same query again.
Console.WriteLine(vbCrLf & "Evens in changed array:")
For Each number In evensQuery2
    Console.Write("  " & number)
Next
Console.WriteLine()

Output:

Evens in original array:

0 2 4 6

Evens in changed array:

0 10 2 22 8

Esecuzione immediata

Nell'esecuzione posticipata delle query la definizione della query viene archiviata in una variabile di query per un'esecuzione successiva. Nell'esecuzione immediata la query viene eseguita contestualmente alla definizione. L'esecuzione viene attivata quando si applica un metodo che richiede l'accesso a singoli elementi del risultato della query. L'esecuzione immediata viene spesso forzata usando uno degli operatori query standard che restituiscono valori singoli, ad esempio Count, Max, Average e First. Questi operatori query standard eseguono la query non appena vengono applicati per calcolare e restituire un risultato singleton. Per altre informazioni sugli operatori query standard che restituiscono valori singoli, vedere Operazioni di aggregazione, Operazioni sugli elementi e Operazioni del quantificatore.

La query seguente restituisce un conteggio dei numeri pari in una matrice di interi. La definizione della query non viene salvata e numEvens è un semplice valore Integer.

Dim numEvens = (From num In numbers
                Where num Mod 2 = 0
                Select num).Count()

È possibile ottenere lo stesso risultato usando il metodo Aggregate.

Dim numEvensAgg = Aggregate num In numbers
                  Where num Mod 2 = 0
                  Select num
                  Into Count()

È anche possibile forzare l'esecuzione di una query chiamando il metodo ToList o ToArray su una query (esecuzione immediata) o una variabile di query (esecuzione posticipata), come illustrato nel codice seguente.

' Immediate execution.
Dim evensList = (From num In numbers
                 Where num Mod 2 = 0
                 Select num).ToList()

' Deferred execution.
Dim evensQuery3 = From num In numbers
                  Where num Mod 2 = 0
                  Select num
' . . .
Dim evensArray = evensQuery3.ToArray()

Negli esempi precedenti evensQuery3 è una variabile di query, ma evensList è un elenco e evensArray è una matrice.

L'uso di ToList o ToArray per forzare l'esecuzione immediata risulta particolarmente utile negli scenari in cui si vuole eseguire subito la query e memorizzare nella cache i risultati in un singolo oggetto raccolta. Per altre informazioni su questi metodi, vedere Conversione dei tipi di dati.

È anche possibile eseguire una query usando un metodo IEnumerable, come IEnumerable.GetEnumerator.

Vedi anche