Пошаговое руководство. Написание запросов на C# (LINQ)
В этом пошаговом руководстве показано использование возможностей C#, которые служат для написания выражений запросов LINQ. После выполнения этого руководства можно переходить к примерам и документации по конкретным поставщикам LINQ, которые вас заинтересовали, например LINQ to SQL, LINQ в набор данных или LINQ to XML.
Обязательные компоненты
Для выполнения этого руководства требуется Visual Studio 2010.
Для просмотра видеоверсии этого раздела перейдите по ссылке Видео: написание запросов в C# (LINQ).
Создание проекта C#
Создание проекта C#, предназначенного для платформы .NET Framework версии 3.5
Запустите Visual Studio.
В меню Файл последовательно выберите пункты Создать и Проект.
В правом верхнем углу диалогового окна Создание проекта находится три значка. Щелкните левый значок и убедитесь, что установлен флажок .NET Framework версии 3.5.
Щелкните значок Консольное приложение в области Установленные шаблоны Visual Studio.
Присвойте приложению новое имя или примите имя по умолчанию и нажмите кнопку ОК.
Обратите внимание, что проект содержит ссылку на 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;
Выполнение запроса
Выполнение запроса
Напишите цикл foreach, который приведет к выполнению запроса. Обратите внимание на следующие моменты.
Доступ к каждому элементу в возвращаемой последовательности осуществляется с помощью переменной итерации в цикле foreach.
Типом этой переменной является Student, а типом переменной запроса является совместимый IEnumerable<Student>.
После добавления этого кода выполните построение и запустите приложение, нажав сочетание клавиш 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#).
Изменение запроса
Упорядочение результатов
Просматривать результаты легче, если они как-то упорядочены. Возвращаемую последовательность можно упорядочить по любому доступному полю в исходных элементах. Например, следующее предложение orderby сортирует результаты в алфавитном порядке от А до Я по фамилии каждого учащегося. Добавьте следующее предложение orderby к запросу, указав его после инструкции where и перед оператором select.
orderby student.Last ascending
Теперь измените предложение orderby таким образом, чтобы оно сортировало результаты в обратном порядке по результату первого тестирования, от высшего к низшему показателю.
orderby student.Scores[0] descending
Измените строку форматирования WriteLine, чтобы видеть результаты тестирования.
Console.WriteLine("{0}, {1} {2}", student.Last, student.First, student.Scores[0]);
Дополнительные сведения см. в разделе Предложение orderby (Справочник по C#).
Группировка результатов
Группировка является мощной возможностью выражений запроса. Запрос с предложением group создает последовательность групп, где каждая группа содержит Key и последовательность, состоящую из всех членов этой группы. Следующий новый запрос группирует учащихся по первой букве их фамилии в качестве ключа.
// studentQuery2 is an IEnumerable<IGrouping<char, Student>> var studentQuery2 = from student in students group student by student.Last[0];
Обратите внимание, что тип запроса изменился. Теперь он создает последовательность из групп, имеющих тип 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); } }
Нажмите сочетание клавиш 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
Очень часто в запросах создаются последовательности, элементы которых отличаются от элементов в исходных последовательностях. Удалите или закомментируйте предыдущий запрос и цикл и замените его следующим кодом. Обратите внимание, что запрос возвращает последовательность строк (не 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); }
Приведенный ранее в этом пошаговом руководстве код показывает, что средний результат для всего класса составляет примерно 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:
См. также
Задачи
Пошаговое руководство. Написание запросов в Visual Basic
Основные понятия
Выражения запросов LINQ (Руководство по программированию на C#)
Дополнительные ресурсы по LINQ