Procedura dettagliata: scrittura di query in C# (LINQ)

In questa procedura guidata vengono illustrate le funzionalità del linguaggio C# utilizzate per scrivere espressioni di query LINQ. Dopo avere completato questa procedura dettagliata sarà possibile passare agli esempi e alla documentazione per il provider LINQ specifico desiderato, ad esempio LINQ to SQL, LINQ to DataSets o LINQ to XML.

Prerequisiti

Questa procedura dettagliata richiede Visual Studio 2010.

Collegamento a video Per una versione video di questo argomento, vedere Procedura relativa a contenuti video: scrittura di query in C# (LINQ) (informazioni in lingua inglese).

Creazione di un progetto C#

Per creare un progetto C# per la versione 3.5 di .NET Framework

  1. Avviare Visual Studio.

  2. Scegliere Nuovo dal menu File, quindi Progetto.

  3. Nell'angolo superiore destro della finestra di dialogo Nuovo progetto sono presenti tre icone. Fare clic sull'icona a sinistra e verificare che sia selezionato .NET Framework versione 3.5.

  4. Fare clic sull'icona Applicazione console sotto Modelli Visual Studio installati.

  5. Assegnare all'applicazione un nuovo nome o accettare il nome predefinito e fare clic su OK.

  6. Il progetto include un riferimento a System.Core.dll e una direttiva using per lo spazio dei nomi System.Linq.

Creazione di un'origine dati in memoria

L'origine dati per le query è un semplice elenco di oggetti Student. Ogni record Student contiene un nome, un cognome e una matrice di valori interi che rappresentano i punteggi dei test della classe. Copiare questo codice nel progetto. Tenere presente le seguenti caratteristiche:

  • La classe Student è costituita da proprietà implementate automaticamente.

  • Ogni studente nell'elenco viene inizializzato con un inizializzatore di oggetto.

  • L'elenco stesso viene inizializzato con un inizializzatore di insieme.

Tutta la struttura dei dati verrà inizializzata e ne verrà creata un'istanza senza chiamate esplicite a un costruttore o accesso esplicito a un membro. Per ulteriori informazioni su queste nuove funzionalità, vedere Proprietà implementate automaticamente (Guida per programmatori C#) e Inizializzatori di oggetto e di insieme (Guida per programmatori C#).

Per aggiungere l'origine dati

  • Aggiungere la classe Student e l'elenco di studenti inizializzato alla classe Program nel progetto.

    public class Student
    {
        public string First { get; set; }
        public string Last { get; set; }
        public int ID { get; set; }
        public List<int> Scores;
    }
    
    // Create a data source by using a collection initializer.
    static List<Student> students = new List<Student>
    {
       new Student {First="Svetlana", Last="Omelchenko", ID=111, Scores= new List<int> {97, 92, 81, 60}},
       new Student {First="Claire", Last="O’Donnell", ID=112, Scores= new List<int> {75, 84, 91, 39}},
       new Student {First="Sven", Last="Mortensen", ID=113, Scores= new List<int> {88, 94, 65, 91}},
       new Student {First="Cesar", Last="Garcia", ID=114, Scores= new List<int> {97, 89, 85, 82}},
       new Student {First="Debra", Last="Garcia", ID=115, Scores= new List<int> {35, 72, 91, 70}},
       new Student {First="Fadi", Last="Fakhouri", ID=116, Scores= new List<int> {99, 86, 90, 94}},
       new Student {First="Hanying", Last="Feng", ID=117, Scores= new List<int> {93, 92, 80, 87}},
       new Student {First="Hugo", Last="Garcia", ID=118, Scores= new List<int> {92, 90, 83, 78}},
       new Student {First="Lance", Last="Tucker", ID=119, Scores= new List<int> {68, 79, 88, 92}},
       new Student {First="Terry", Last="Adams", ID=120, Scores= new List<int> {99, 82, 81, 79}},
       new Student {First="Eugene", Last="Zabokritski", ID=121, Scores= new List<int> {96, 85, 91, 60}},
       new Student {First="Michael", Last="Tucker", ID=122, Scores= new List<int> {94, 92, 91, 91} }
    };
    

Per aggiungere un nuovo oggetto Student all'elenco Students

  • Aggiungere un nuovo oggetto Student all'elenco Students e utilizzare un nome e i punteggi dei test desiderati. Provare a immettere tutte le informazioni sul nuovo studente in modo da acquisire dimestichezza con la sintassi per l'inizializzatore di oggetto.

Creazione della query

Per creare una query semplice

  • Nel metodo Main dell'applicazione creare una query semplice che, quando verrà eseguita, produrrà un elenco di tutti gli studenti il cui punteggio nel primo test era superiore a 90. Si noti che, poiché viene selezionato l'intero oggetto Student, il tipo della query è IEnumerable<Student>. Sebbene nel codice poteva essere utilizzata la tipizzazione implicita mediante la parola chiave var, è stata utilizzata la tipizzazione esplicita per illustrare dettagliatamente i risultati. Per ulteriori informazioni su var, vedere Variabili locali tipizzate in modo implicito (Guida per programmatori C#).

    Tenere inoltre presente che la variabile di intervallo della query, student, funge da riferimento a ogni oggetto Student presente nell'origine, fornendo l'accesso al membro per ogni oggetto.


// Create the query.
// The first line could also be written as "var studentQuery ="
IEnumerable<Student> studentQuery =
    from student in students
    where student.Scores[0] > 90
    select student;

Esecuzione della query

Per eseguire la query

  1. Scrivere ora il ciclo foreach che consente di eseguire la query. In relazione al codice tenere presente quanto segue:

    • È possibile accedere a ogni elemento della sequenza restituita mediante la variabile di iterazione nel ciclo foreach.

    • Il tipo di questa variabile è Student e il tipo della variabile di query è compatibile, IEnumerable<Student>.

  2. Dopo avere aggiunto questo codice, compilare ed eseguire l'applicazione premendo CTRL + F5 per visualizzare i risultati nella finestra della console.

// Execute the query.
// var could be used here also.
foreach (Student student in studentQuery)
{
    Console.WriteLine("{0}, {1}", student.Last, student.First);
}

Per aggiungere un'altra condizione di filtro

  • È possibile combinare più condizioni booleane nella clausola where in modo da perfezionare ulteriormente la query. Il codice seguente aggiunge una condizione in modo che la query restituisca gli studenti di cui il primo punteggio era superiore a 90 e l'ultimo inferiore a 80. La clausola where dovrebbe essere analoga al codice seguente.

    where student.Scores[0] > 90 && student.Scores[3] < 80
    

    Per ulteriori informazioni, vedere Clausola where (Riferimento C#).

Modifica della query

Per ordinare i risultati

  1. Sarà più semplice analizzare i risultati se vengono ordinati. È possibile ordinare la sequenza restituita in base a qualsiasi campo accessibile negli elementi di origine. Ad esempio, la clausola orderby riportata di seguito ordina i risultati alfabeticamente dalla A alla Z in base al cognome di ogni studente. Aggiungere alla query la clausola orderby riportata di seguito, subito dopo l'istruzione where e prima dell'istruzione select:

    orderby student.Last ascending
    
  2. Modificare ora la clausola orderby in modo da disporre i risultati in ordine inverso in base al punteggio del primo test, iniziando dal punteggio più alto fino a quello più basso.

    orderby student.Scores[0] descending
    
  3. Modificare la stringa di formato WriteLine in modo da poter visualizzare i punteggi:

    Console.WriteLine("{0}, {1} {2}", student.Last, student.First, student.Scores[0]);
    

    Per ulteriori informazioni, vedere Clausola orderby (Riferimento C#).

Per raggruppare i risultati

  1. Il raggruppamento è una funzionalità potente nelle espressioni di query. Una query con una clausola group genera una sequenza di gruppi e ogni gruppo contiene un oggetto Key e una sequenza costituita da tutti i membri di tale gruppo. Nella nuova query riportata di seguito gli studenti vengono raggruppati utilizzando la prima lettera del cognome come chiave.

    // studentQuery2 is an IEnumerable<IGrouping<char, Student>>
    var studentQuery2 =
        from student in students
        group student by student.Last[0];
    
  2. Il tipo della query è stato ora modificato. Vengono ora generate una sequenza di gruppi con il tipo char come chiave e una sequenza di oggetti Student. Poiché il tipo della query è stato modificato, nel codice seguente viene modificato anche il ciclo di esecuzione foreach:

    // studentGroup is a IGrouping<char, Student>
    foreach (var studentGroup in studentQuery2)
    {
        Console.WriteLine(studentGroup.Key);
        foreach (Student student in studentGroup)
        {
            Console.WriteLine("   {0}, {1}",
                      student.Last, student.First);
        }
    }
    
  3. Premere CTRL + F5 per eseguire l'applicazione e visualizzare i risultati nella finestra della console.

    Per ulteriori informazioni, vedere Clausola group (Riferimento C#).

Per creare le variabili tipizzate in modo implicito

  • Codificare in modo esplicito IEnumerables di IGroupings può risultare noioso. È possibile scrivere la stessa query e il ciclo foreach in modo notevolmente più pratico utilizzando var. La parola chiave var non modifica i tipi degli oggetti ma indica solo al compilatore di dedurre i tipi. Impostare il tipo di studentQuery e la variabile di iterazione group su var e rieseguire la query. Nel ciclo foreach interno la variabile di iterazione è ancora tipizzata come Student e la query funziona esattamente come prima. Impostare la variabile di iterazione s su var e rieseguire la query. Si otterranno esattamente gli stessi risultati.

    var studentQuery3 =
        from student in students
        group student by student.Last[0];
    
    foreach (var groupOfStudents in studentQuery3)
    {
        Console.WriteLine(groupOfStudents.Key);
        foreach (var student in groupOfStudents)
        {
             Console.WriteLine("   {0}, {1}",
                 student.Last, student.First);
        }
    }
    

    Per ulteriori informazioni su var, vedere Variabili locali tipizzate in modo implicito (Guida per programmatori C#).

Per ordinare i gruppi in base al valore della chiave

  • Quando si esegue la query precedente, i gruppi non sono in ordine alfabetico. Per modificare questo comportamento, è necessario fornire la clausola orderby dopo la clausola group. Per utilizzare la clausola orderby, è necessario però utilizzare prima un identificatore che funga da riferimento ai gruppi creati dalla clausola group. Fornire l'identificatore utilizzando la parola chiave into, come segue:

                var studentQuery4 =
                    from student in students
                    group student by student.Last[0] into studentGroup
                    orderby studentGroup.Key
                    select studentGroup;
    
                foreach (var groupOfStudents in studentQuery4)
                {
                    Console.WriteLine(groupOfStudents.Key);
                    foreach (var student in groupOfStudents)
                    {
                        Console.WriteLine("   {0}, {1}",
                            student.Last, student.First);
                    }
                }
    
    

    Quando si esegue questa query, i gruppi verranno ora ordinati alfabeticamente.

Per introdurre un identificatore utilizzando let

  • È possibile utilizzare la parola chiave let per introdurre un identificatore per qualsiasi risultato dell'espressione di query. Questo identificatore può risultare utile come nell'esempio seguente o può migliorare le prestazioni archiviando i risultati di un'espressione in modo da non doverla calcolare più volte.

    // studentQuery5 is an IEnumerable<string>
    // This query returns those students whose
    // first test score was higher than their
    // average score.
    var studentQuery5 =
        from student in students
        let totalScore = student.Scores[0] + student.Scores[1] +
            student.Scores[2] + student.Scores[3]
        where totalScore / 4 < student.Scores[0]
        select student.Last + " " + student.First;
    
    foreach (string s in studentQuery5)
    {
        Console.WriteLine(s);
    }
    

    Per ulteriori informazioni, vedere Clausola let (Riferimento C#).

Per utilizzare la sintassi del metodo in un'espressione di query

  • Come descritto in Confronto tra sintassi di query LINQ e sintassi dei metodi (C#), alcune operazioni di query possono essere espresse solo utilizzando la sintassi del metodo. Nel codice seguente viene calcolato il punteggio totale per ogni Student nella sequenza di origine e viene quindi chiamato il metodo Average() sui risultati della query per calcolare il punteggio medio della classe. Notare l'utilizzo delle parentesi nell'espressione di query.

        var studentQuery6 = 
            from student in students
            let totalScore = student.Scores[0] + student.Scores[1] +
                student.Scores[2] + student.Scores[3]
            select totalScore;
    
        double averageScore = studentQuery6.Average();
    Console.WriteLine("Class average score = {0}", averageScore);
    

Per trasformare o proiettare nella clausola select

  1. Una query genera normalmente una sequenza i cui elementi differiscono dagli elementi delle sequenze di origine. Eliminare o impostare come commento la query precedente e il ciclo di esecuzione e sostituirli con il codice seguente. La query restituisce una sequenza di stringhe (non Students) che si riflette nel ciclo foreach.

                IEnumerable<string> studentQuery7 =
                    from student in students
                    where student.Last == "Garcia"
                    select student.First;
    
                Console.WriteLine("The Garcias in the class are:");
                foreach (string s in studentQuery7)
                {
                    Console.WriteLine(s);
                }
    
    
  2. Il codice precedente in questa procedura dettagliata indica che il punteggio medio della classe è pari a circa 334. Per produrre una sequenza di Students il cui punteggio totale sia superiore alla media della classe, insieme al relativo Student ID, è possibile utilizzare un tipo anonimo nell'istruzione select:

                var studentQuery8 =
                    from student in students
                    let x = student.Scores[0] + student.Scores[1] +
                        student.Scores[2] + student.Scores[3]
                    where x > averageScore
                    select new { id = student.ID, score = x };
    
                foreach (var item in studentQuery8)
                {
                    Console.WriteLine("Student ID: {0}, Score: {1}", item.id, item.score);
                }
    
    

Passaggi successivi

Dopo aver acquisito dimestichezza con le caratteristiche principali delle query in C#, è possibile leggere la documentazione e gli esempi per il tipo specifico di provider LINQ desiderato:

LINQ to SQL

LINQ to DataSet

LINQ to XML

LINQ to Objects

Vedere anche

Attività

Procedura dettagliata: scrittura delle query in Visual Basic

Concetti

LINQ Query Expressions (C# Programming Guide)

Risorse LINQ supplementari

Altre risorse

LINQ (Language-Integrated Query)

Nozioni di base su LINQ in C#