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

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

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

Для выполнения этого руководства требуется Visual Studio 2010.

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

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

Создание проекта C#, предназначенного для платформы .NET Framework версии 3.5

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

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

  3. В правом верхнем углу диалогового окна Создание проекта находится три значка. Щелкните левый значок и убедитесь, что установлен флажок .NET Framework версии 3.5.

  4. Щелкните значок Консольное приложение в области Установленные шаблоны Visual Studio.

  5. Присвойте приложению новое имя или примите имя по умолчанию и нажмите кнопку ОК.

  6. Обратите внимание, что проект содержит ссылку на 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);
}

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

  • Чтобы уточнить запрос, можно объединить несколько логических условий в предложении 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);
        }
    }
    
  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);
        }
    }
    

    Более подробную информацию о 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);
                    }
                }
    
    

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

Введение идентификаторов с помощью 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);
    }
    

    Дополнительные сведения см. в разделе Предложение 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);
    

Преобразование или проецирование в предложении 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);
                }
    
    
  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);
                }
    
    

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

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

LINQ to SQL

LINQ to DataSet

LINQ to XML

LINQ to Objects

См. также

Задачи

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

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

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

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

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

LINQ

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