Пошаговое руководство. Написание запросов на C# (LINQ)

В этом пошаговом руководстве показано использование возможностей C#, которые служат для написания выражений запросов LINQ. После выполнения этого руководства можно переходить к примерам и документации по конкретным поставщикам LINQ, которые вас заинтересовали, например LINQ to SQL, LINQ в набор данных или LINQ to XML.

Обязательные компоненты

В данном пошаговом руководстве требуется функции, представленные в Visual Studio 2008.

ссылка на видео Для просмотра видеоверсии этого раздела перейдите по ссылке Видео: написание запросов в C# (LINQ).

Создание проекта C#

Создание проекта

  1. Запустите Visual Studio.

  2. В строке меню выберите Файл, Создать, Проект.

    Откроется диалоговое окно Новый проект.

  3. Разверните узел Установлено, разверните Шаблоны, разверните Visual C#, а затем выберите Консольное приложение.

  4. В текстовом поле Имя введите другое имя или примите имя по умолчанию, а затем нажмите кнопку ОК.

    В обозревателе решений появится новый проект.

  5. Обратите внимание, что проект содержит ссылку на System.Core.dll и директиву using для пространства имен System.Linq.

Создание расположенного в памяти источника данных

Источником данных для запросов является простой список объектов Student. Каждая запись Student имеет имя, фамилию и массив целых чисел, представляющий результаты тестирования в классе. Скопируйте этот код в проект. Обратите внимание на следующие характеристики.

  • Класс Student состоит из автоматически реализованных свойств.

  • Каждый учащийся в списке инициализируется с помощью инициализатора объектов.

  • Сам список инициализируется с помощью инициализатора коллекцией.

Вся эта структура данных будет инициализирована и создана без явных вызовов конструкторов или явного доступа к членам. Дополнительные сведения об этих новых возможностях см. в разделах Автоматически реализуемые свойства (Руководство по программированию на C#) и Инициализаторы объектов и коллекций (Руководство по программированию в C#).

Добавление источника данных

  • Добавьте класс Student и инициализированный список учащихся к классу Program в проекте.

    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} }
    };
    

Добавление нового учащегося в список учащихся

  • Добавьте нового Student в список Students и используйте любое имя и результаты тестирования. Попробуйте набрать всю информацию о новом учащемся, чтобы лучше понять синтаксис инициализатора объектов.

Создание запроса

Создание простого запроса

  • В методе Main создайте простой запрос, который при выполнении вернет список всех учащихся, результат тестирования которых превысил 90 баллов. Обратите внимание, что поскольку объект Student выбирается целиком, типом запроса будет IEnumerable<Student>. Хотя код мог также использовать неявную типизацию при помощи ключевого слова var, используется явная типизация, чтобы ясно показать результаты. Дополнительные сведения о var см. в разделе Неявно типизированные локальные переменные (Руководство по программированию в C#).

    Обратите внимание, что переменная диапазона запроса student служит ссылкой на каждый объект Student в источнике, предоставляя доступ к членам для каждого объекта.

// 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;

Выполнение запроса

Для выполнения запроса:

  1. Напишите цикл foreach, который приведет к выполнению запроса. Обратите внимание на следующие моменты.

    • Доступ к каждому элементу в возвращаемой последовательности осуществляется с помощью переменной итерации в цикле foreach.

    • Типом этой переменной является Student, а типом переменной запроса является совместимый IEnumerable<Student>.

  2. После добавления этого кода выполните построение и запустите приложение, нажав сочетание клавиш Ctrl + F5 для просмотра результатов в окне Консоль.

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

// Output: 
// Omelchenko, Svetlana 
// Garcia, Cesar 
// Fakhouri, Fadi 
// Feng, Hanying 
// Garcia, Hugo 
// Adams, Terry 
// Zabokritski, Eugene 
// Tucker, Michael

Добавление дополнительного условия фильтра

  • Чтобы уточнить запрос, можно объединить несколько логических условий в предложении where. Следующий код добавляет условие таким образом, чтобы запрос возвращал тех учащихся, первый результат которых был более 90 баллов, а последний результат был меньше 80. Предложение where должно выглядеть следующим образом.

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

    Дополнительные сведения см. в разделе Предложение where (Справочник по C#).

Изменение запроса

Упорядочение результатов

  1. Просматривать результаты легче, если они как-то упорядочены. Возвращаемую последовательность можно упорядочить по любому доступному полю в исходных элементах. Например, следующее предложение orderby сортирует результаты в алфавитном порядке от А до Я по фамилии каждого учащегося. Добавьте следующее предложение orderby к запросу, указав его после инструкции where и перед оператором select.

    orderby student.Last ascending
    
  2. Теперь измените предложение orderby таким образом, чтобы оно сортировало результаты в обратном порядке по результату первого тестирования, от высшего к низшему показателю.

    orderby student.Scores[0] descending
    
  3. Измените строку форматирования WriteLine, чтобы видеть результаты тестирования.

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

    Дополнительные сведения см. в разделе Предложение orderby (Справочник по C#).

Группировка результатов

  1. Группировка является мощной возможностью выражений запроса. Запрос с предложением group создает последовательность групп, где каждая группа содержит Key и последовательность, состоящую из всех членов этой группы. Следующий новый запрос группирует учащихся по первой букве их фамилии в качестве ключа.

    // studentQuery2 is an IEnumerable<IGrouping<char, Student>> 
    var studentQuery2 =
        from student in students
        group student by student.Last[0];
    
  2. Обратите внимание, что тип запроса изменился. Теперь он создает последовательность из групп, имеющих тип char в качестве ключа, и последовательность объектов Student. Поскольку тип запроса изменился, следующий код изменяет также и цикл 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);
        }
    }
    
    // Output: 
    // O 
    //   Omelchenko, Svetlana 
    //   O'Donnell, Claire 
    // M 
    //   Mortensen, Sven 
    // G 
    //   Garcia, Cesar 
    //   Garcia, Debra 
    //   Garcia, Hugo 
    // F 
    //   Fakhouri, Fadi 
    //   Feng, Hanying 
    // T 
    //   Tucker, Lance 
    //   Tucker, Michael 
    // A 
    //   Adams, Terry 
    // Z 
    //   Zabokritski, Eugene
    
  3. Нажмите сочетание клавиш Ctrl + F5 для выполнения приложения и просмотра результатов в окне Консоль.

    Дополнительные сведения см. в разделе Предложение group (Справочник по C#).

Присвоение переменной неявного типа

  • Явное кодирование IEnumerables из IGroupings может быстро стать трудоемким. С помощью var можно более просто написать тот же запрос и цикл foreach. Ключевое слово var не приводит к изменению типов объектов; оно просто сообщает компилятору о необходимости определения типов. Измените тип studentQuery и переменная итерации group к var и повторно выполните запрос. Обратите внимание, что во внутреннем цикле foreach переменная итерации по-прежнему типизирована как Student и запрос работает так же, как и раньше. Измените переменную итерации s на var и повторно выполните запрос. Результаты остались неизменными.

    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);
        }
    }
    
    // Output: 
    // O 
    //   Omelchenko, Svetlana 
    //   O'Donnell, Claire 
    // M 
    //   Mortensen, Sven 
    // G 
    //   Garcia, Cesar 
    //   Garcia, Debra 
    //   Garcia, Hugo 
    // F 
    //   Fakhouri, Fadi 
    //   Feng, Hanying 
    // T 
    //   Tucker, Lance 
    //   Tucker, Michael 
    // A 
    //   Adams, Terry 
    // Z 
    //   Zabokritski, Eugene
    

    Более подробную информацию о var см. в разделе Неявно типизированные локальные переменные (Руководство по программированию в C#).

Упорядочение групп по значению их ключа

  • При выполнении предыдущего запроса группы были расположены не в алфавитном порядке. Для упорядочения необходимо указать предложение orderby после предложения group. Но, чтобы использовать предложение orderby, нужен идентификатор, служащий в качестве ссылки на группы, создаваемые предложением group. Предоставить идентификатор можно с помощью ключевого слова into следующим образом.

    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);
        }
    }
    
    // Output: 
    //A 
    //   Adams, Terry 
    //F 
    //   Fakhouri, Fadi 
    //   Feng, Hanying 
    //G 
    //   Garcia, Cesar 
    //   Garcia, Debra 
    //   Garcia, Hugo 
    //M 
    //   Mortensen, Sven 
    //O 
    //   Omelchenko, Svetlana 
    //   O'Donnell, Claire 
    //T 
    //   Tucker, Lance 
    //   Tucker, Michael 
    //Z 
    //   Zabokritski, Eugene
    

    При выполнении этого запроса группы будут отсортированы в алфавитном порядке.

Введение идентификаторов с помощью let

  • Ключевое слово let можно использовать для представления идентификатора для любого результата выражения в выражении запроса. Этот идентификатор может применяться для удобства, как в следующем примере, или он может повысить производительность, сохраняя результаты выражения так, чтобы оно не вычислялось повторно.

    // 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);
    }
    
    // Output: 
    // Omelchenko Svetlana 
    // O'Donnell Claire 
    // Mortensen Sven 
    // Garcia Cesar 
    // Fakhouri Fadi 
    // Feng Hanying 
    // Garcia Hugo 
    // Adams Terry 
    // Zabokritski Eugene 
    // Tucker Michael
    

    Дополнительные сведения см. в разделе Предложение let (справочник по C#).

Использование синтаксиса метода в выражении запроса

  • Как отмечалось в Синтаксис запросов и синтаксис методов в LINQ (C#), некоторые операции запроса могут быть выражены только с помощью синтаксиса методов. В следующем коде вычисляется общий результат для каждого Student в исходной последовательности, а затем вызывается метод Average(), использующий результаты запроса для вычисления среднего балла класса. Обратите внимание на круглые скобки вокруг выражения запроса.

    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);
    
    // Output: 
    // Class average score = 334.166666666667
    

Преобразование или проецирование в предложении select

  1. Очень часто в запросах создаются последовательности, элементы которых отличаются от элементов в исходных последовательностях. Удалите или закомментируйте предыдущий запрос и цикл и замените его следующим кодом. Обратите внимание, что запрос возвращает последовательность строк (не Students), и этот факт отражается в цикле 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);
    }
    
    // Output: 
    // The Garcias in the class are: 
    // Cesar 
    // Debra 
    // Hugo
    
  2. Приведенный ранее в этом пошаговом руководстве код показывает, что средний результат для всего класса составляет примерно 334. Для создания последовательности Students, суммарный результат баллов которых больше среднего, вместе с их Student ID, можно использовать анонимный тип в инструкции 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);
    }
    
    // Output: 
    // Student ID: 113, Score: 338 
    // Student ID: 114, Score: 353 
    // Student ID: 116, Score: 369 
    // Student ID: 117, Score: 352 
    // Student ID: 118, Score: 343 
    // Student ID: 120, Score: 341 
    // Student ID: 122, Score: 368
    

Следующие действия

После ознакомления с основными аспектами работы с запросами в C# можно приступить к чтению документации и просмотру примеров для конкретного типа поставщика LINQ:

LINQ to SQL [LINQ to SQL]

LINQ to DataSet

LINQ to XML

LINQ to Objects

См. также

Задачи

Пошаговое руководство. Написание запросов в Visual Basic

Основные понятия

Выражения запросов LINQ (Руководство по программированию на C#)

Дополнительные ресурсы по LINQ

Другие ресурсы

LINQ

Приступая к работе с LINQ в C#