Поделиться через


Обзор операторов стандартных запросов

Стандартные операторы запросов — это ключевые слова и методы, которые формируют шаблон LINQ. Язык C# определяет ключевые слова запроса LINQ , которые используются для наиболее распространенного выражения запроса. Компилятор преобразует выражения с помощью этих ключевых слов в эквивалентные вызовы метода. Две формы являются синонимами. Другие методы, которые являются частью System.Linq пространства имен, не имеют эквивалентных ключевых слов запроса. В этих случаях необходимо использовать синтаксис метода. В этом разделе рассматриваются все ключевые слова оператора запроса. Среда выполнения и другие пакеты NuGet с каждым выпуском добавляют дополнительные методы, предназначенные для работы с запросами LINQ. Наиболее распространенные методы, включая те, которые имеют эквиваленты ключевых слов запроса, рассматриваются в этом разделе. Полный список методов запросов, поддерживаемых средой выполнения .NET, см. в System.Linq.Enumerable документации по API. Помимо методов, описанных здесь, этот класс содержит методы для объединения источников данных, вычисления одного значения из источника данных, например суммы, среднего или другого значения.

Это важно

В этих примерах используется System.Collections.Generic.IEnumerable<T> источник данных. Источники данных, основанные на System.Linq.IQueryProvider, используют System.Linq.IQueryable<T> источники данных и деревья выражений. Деревья выражений имеют ограничения на допустимый синтаксис C#. Кроме того, каждый IQueryProvider источник данных, например EF Core , может наложить больше ограничений. Ознакомьтесь с документацией по источнику данных.

Большинство этих методов работают на последовательностях, где последовательность является объектом, тип которого реализует IEnumerable<T> интерфейс или IQueryable<T> интерфейс. Стандартные операторы запросов предоставляют возможности запроса, включая фильтрацию, проекцию, агрегирование, сортировку и многое другое. Методы, составляющие каждый набор, являются статическими элементами Enumerable и Queryable классами соответственно. Они определяются как методы расширения типа, на который они работают.

Различие между IEnumerable<T> и IQueryable<T> последовательностями определяет, как выполняется запрос во время выполнения.

Для IEnumerable<T> возвращаемый перечисляемый объект сохраняет аргументы, переданные методу. При перечислении этого объекта используется логика оператора запроса и возвращаются результаты запроса.

Для IQueryable<T>, этот запрос преобразуется в дерево выражений. Дерево выражений можно преобразовать в собственный запрос, когда источник данных может оптимизировать запрос. Библиотеки, такие как Entity Framework , преобразуют запросы LINQ в собственные SQL-запросы, которые выполняются в базе данных.

В следующем примере кода показано, как стандартные операторы запросов можно использовать для получения сведений о последовательности.

string sentence = "the quick brown fox jumps over the lazy dog";
// Split the string into individual words to create a collection.
string[] words = sentence.Split(' ');

// Using query expression syntax.
var query = from word in words
            group word.ToUpper() by word.Length into gr
            orderby gr.Key
            select new { Length = gr.Key, Words = gr };

// Using method-based query syntax.
var query2 = words.
    GroupBy(w => w.Length, w => w.ToUpper()).
    Select(g => new { Length = g.Key, Words = g }).
    OrderBy(o => o.Length);

foreach (var obj in query)
{
    Console.WriteLine($"Words of length {obj.Length}:");
    foreach (string word in obj.Words)
        Console.WriteLine(word);
}

// This code example produces the following output:
//
// Words of length 3:
// THE
// FOX
// THE
// DOG
// Words of length 4:
// OVER
// LAZY
// Words of length 5:
// QUICK
// BROWN
// JUMPS

По возможности запросы в этом разделе используют последовательность слов или чисел в качестве источника входных данных. Для запросов, где используются более сложные связи между объектами, используются следующие источники, моделиирующие школу:

public enum GradeLevel
{
    FirstYear = 1,
    SecondYear,
    ThirdYear,
    FourthYear
};

public class Student
{
    public required string FirstName { get; init; }
    public required string LastName { get; init; }
    public required int ID { get; init; }

    public required GradeLevel Year { get; init; }
    public required List<int> Scores { get; init; }

    public required int DepartmentID { get; init; }
}

public class Teacher
{
    public required string First { get; init; }
    public required string Last { get; init; }
    public required int ID { get; init; }
    public required string City { get; init; }
}

public class Department
{
    public required string Name { get; init; }
    public int ID { get; init; }

    public required int TeacherID { get; init; }
}

Каждый Student имеет класс, первичный отдел и ряд оценок. Также у Teacher есть свойство City, определяющее кампус, где учитель ведёт занятия. У Department него есть имя и ссылка на Teacher того, кто выступает в качестве руководителя отдела.

Набор данных можно найти в исходном репозитории.

Типы операторов запросов

Стандартные операторы запросов различаются в зависимости от того, возвращают ли они однотонное значение или последовательность значений. Эти методы, возвращающие одноэлементное значение (например Average , и Sum) выполняются немедленно. Методы, возвращающие последовательность, откладывают выполнение запроса и возвращают перечислимый объект. Вы можете использовать выходную последовательность одного запроса в качестве входной последовательности для другого запроса. Вызовы методов запроса можно объединить в один запрос, что позволяет запросам становиться произвольным сложным.

Операторы запроса

В запросе LINQ сначала необходимо указать источник данных. Предложение from LINQ будет первым в запросе, чтобы ввести источник данных (students) и переменную диапазона (student).

//queryAllStudents is an IEnumerable<Student>
var queryAllStudents = from student in students
                        select student;

Переменная диапазона похожа на переменную итерации в foreach цикле, за исключением того, что фактические итерации не выполняются в выражении запроса. При выполнении запроса переменная диапазона служит ссылкой на каждый последовательный элемент в students. Так как компилятор может выводить тип student, его не нужно указывать явным образом. В предложении let можно ввести дополнительные переменные диапазона. Дополнительные сведения см. в предложении 'let' .

Примечание.

Для не универсальных источников данных, таких как ArrayList, переменная диапазона должна быть явно типизированной. Дополнительные сведения см. в статье "Запрос массива" с помощью LINQ (C#) и предложения.

После получения источника данных можно выполнить любое количество операций с этим источником данных:

Таблица синтаксиса выражения запроса

В следующей таблице перечислены стандартные операторы запросов, имеющие эквивалентные предложения выражения запроса.

Метод Синтаксис выражения запроса C#
Cast Используйте явно типизированные переменные диапазона:

from int i in numbers

(Дополнительные сведения см. в разделе from clause.)
GroupBy group … by

-или-

group … by … into …

(Дополнительные сведения см. в предложении группы.)
GroupJoin<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,IEnumerable<TInner>, TResult>) join … in … on … equals … into …

(Дополнительные сведения см. в клауза объединения.)
Join<TOuter,TInner,TKey,TResult>(IEnumerable<TOuter>, IEnumerable<TInner>, Func<TOuter,TKey>, Func<TInner,TKey>, Func<TOuter,TInner,TResult>) join … in … on … equals …

(Дополнительные сведения см. в предложении объединения.)
OrderBy<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby

(Дополнительные сведения см. в предложении ORDER BY.)
OrderByDescending<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) orderby … descending

(Дополнительные сведения см. в предложении Orderby.)
Select select

(Дополнительные сведения см. в операторе select.)
SelectMany Несколько from клауз.

(Дополнительные сведения см. в предложении.)
ThenBy<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, …

(Дополнительные сведения см. в предложении orderby.)
ThenByDescending<TSource,TKey>(IOrderedEnumerable<TSource>, Func<TSource,TKey>) orderby …, … descending

(Дополнительные сведения см. в конструкции ORDER BY.)
Where where

(Дополнительные сведения см. в предложении where.)

Преобразования данных с помощью LINQ

Language-Integrated LINQ касается не только извлечения данных. Это также мощный инструмент для преобразования данных. С помощью запроса LINQ можно использовать исходную последовательность в качестве входных данных и изменять ее различными способами для создания новой выходной последовательности. Вы можете сортировать и группировать, чтобы изменить саму последовательность, не изменяя сами элементы. Но, возможно, самая мощная функция запросов LINQ — это возможность создавать новые типы. Предложение select создает выходной элемент из входного элемента. Он используется для преобразования входного элемента в выходной элемент:

  • Слияние нескольких входных последовательностей в одну выходную последовательность с новым типом.
  • Создайте выходные последовательности, элементы которых состоят только из одного или нескольких свойств каждого элемента в исходной последовательности.
  • Создайте выходные последовательности, элементы которых состоят из результатов операций, выполняемых в исходных данных.
  • Создайте выходные последовательности в другом формате. Например, можно преобразовать данные из строк SQL или текстовых файлов в XML.

Эти преобразования можно объединить различными способами в одном запросе. Кроме того, выходная последовательность одного запроса может использоваться в качестве входной последовательности для нового запроса. В следующем примере объекты в структуре данных в памяти преобразуются в XML-элементы.


// Create the query.
var studentsToXML = new XElement("Root",
    from student in students
    let scores = string.Join(",", student.Scores)
    select new XElement("student",
                new XElement("First", student.FirstName),
                new XElement("Last", student.LastName),
                new XElement("Scores", scores)
            ) // end "student"
        ); // end "Root"

// Execute the query.
Console.WriteLine(studentsToXML);

Код создает следующие выходные данные XML:

<Root>
  <student>
    <First>Svetlana</First>
    <Last>Omelchenko</Last>
    <Scores>97,90,73,54</Scores>
  </student>
  <student>
    <First>Claire</First>
    <Last>O'Donnell</Last>
    <Scores>56,78,95,95</Scores>
  </student>
  ...
  <student>
    <First>Max</First>
    <Last>Lindgren</Last>
    <Scores>86,88,96,63</Scores>
  </student>
  <student>
    <First>Arina</First>
    <Last>Ivanova</Last>
    <Scores>93,63,70,80</Scores>
  </student>
</Root>

Дополнительные сведения см. в статье "Создание деревьев XML в C# (LINQ to XML)".

Результаты одного запроса можно использовать в качестве источника данных для последующего запроса. В этом примере показано, как упорядочить результаты операции соединения. Этот запрос создает соединение группы, а затем сортирует группы на основе элемента категории, который всё ещё находится в контексте. Внутри инициализатора анонимного типа вложенный запрос отсортировывает все элементы, соответствующие условию, из последовательности продуктов.

var orderedQuery = from department in departments
                   join student in students on department.ID equals student.DepartmentID into studentGroup
                   orderby department.Name
                   select new
                   {
                       DepartmentName = department.Name,
                       Students = from student in studentGroup
                                  orderby student.LastName
                                    select student
                   };

foreach (var departmentList in orderedQuery)
{
    Console.WriteLine(departmentList.DepartmentName);
    foreach (var student in departmentList.Students)
    {
        Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
    }
}
/* Output:
Chemistry
  Balzan     Josephine
  Fakhouri   Fadi
  Popov      Innocenty
  Seleznyova Sofiya
  Vella      Carmen
Economics
  Adams      Terry
  Adaobi     Izuchukwu
  Berggren   Jeanette
  Garcia     Cesar
  Ifeoma     Nwanneka
  Jamuike    Ifeanacho
  Larsson    Naima
  Svensson   Noel
  Ugomma     Ifunanya
Engineering
  Axelsson   Erik
  Berg       Veronika
  Engström   Nancy
  Hicks      Cassie
  Keever     Bruce
  Micallef   Nicholas
  Mortensen  Sven
  Nilsson    Erna
  Tucker     Michael
  Yermolayeva Anna
English
  Andersson  Sarah
  Feng       Hanying
  Ivanova    Arina
  Jakobsson  Jesper
  Jensen     Christiane
  Johansson  Mark
  Kolpakova  Nadezhda
  Omelchenko Svetlana
  Urquhart   Donald
Mathematics
  Frost      Gaby
  Garcia     Hugo
  Hedlund    Anna
  Kovaleva   Katerina
  Lindgren   Max
  Maslova    Evgeniya
  Olsson     Ruth
  Sammut     Maria
  Sazonova   Anastasiya
Physics
  Åkesson    Sami
  Edwards    Amy E.
  Falzon     John
  Garcia     Debra
  Hansson    Sanna
  Mattsson   Martina
  Richardson Don
  Zabokritski Eugene
*/

Эквивалентный запрос с использованием синтаксиса метода показан в следующем коде:

var orderedQuery = departments
    .GroupJoin(students, department => department.ID, student => student.DepartmentID,
    (department, studentGroup) => new
    {
        DepartmentName = department.Name,
        Students = studentGroup.OrderBy(student => student.LastName)
    })
    .OrderBy(department => department.DepartmentName);


foreach (var departmentList in orderedQuery)
{
    Console.WriteLine(departmentList.DepartmentName);
    foreach (var student in departmentList.Students)
    {
        Console.WriteLine($"  {student.LastName,-10} {student.FirstName,-10}");
    }
}

Хотя вы можете использовать условие orderby с одной или несколькими исходными последовательностями перед присоединением, обычно мы не рекомендуем это. Некоторые поставщики LINQ могут не сохранять порядок после соединения. Для получения дополнительных сведений см. предложение соединения .

См. также