Запись запросов LINQ C# для запроса данных

Большинство запросов в вводной документации к LINQ написано с использованием декларативного синтаксиса запросов LINQ. Однако синтаксис запроса должен быть преобразован в вызовы методов для среды CLR .NET, когда код компилируется. Эти вызовы метода вызывают стандартные операторы запросов, которые имеют такие имена, как Where, Select, GroupBy, Join, Max и Average. Вместо синтаксиса запросов для их вызова можно использовать синтаксис методов.

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

Стандартные методы расширения оператора запросов

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

int[] numbers = [ 5, 10, 8, 3, 6, 12 ];

//Query syntax:
IEnumerable<int> numQuery1 =
    from num in numbers
    where num % 2 == 0
    orderby num
    select num;

//Method syntax:
IEnumerable<int> numQuery2 = numbers.Where(num => num % 2 == 0).OrderBy(n => n);

foreach (int i in numQuery1)
{
    Console.Write(i + " ");
}
Console.WriteLine(System.Environment.NewLine);
foreach (int i in numQuery2)
{
    Console.Write(i + " ");
}

Оба примера дают одинаковый результат. Видно, что тип переменной запроса в обеих формах одинаковый: IEnumerable<T>.

Чтобы разобраться в запросе, основанном на методе, изучим его более подробно. В правой части выражения обратите внимание, что where предложение теперь выражается как метод экземпляра объекта numbers , который имеет тип IEnumerable<int>. Если вы знакомы с универсальным IEnumerable<T> интерфейсом Where , вы знаете, что у него нет метода. Однако при вызове списка завершения IntelliSense в интегрированной среде разработки Visual Studio вы увидите не только Where метод, но и многие другие методы, такие как Select, JoinSelectManyи Orderby. Эти методы реализуют стандартные операторы запросов.

Screenshot showing all the standard query operators in Intellisense.

Хотя он выглядит так, как будто IEnumerable<T> включает дополнительные методы, это не так. Стандартные операторы запросов реализуются как методы расширения. Эти методы "расширяют" существующий тип и могут вызываться так, как если бы они являлись методами экземпляра для этого типа. Стандартные операторы запроса расширяют IEnumerable<T>, и поэтому вы можете написать numbers.Where(...).

Чтобы использовать методы расширения, их можно перенести в область с using директивами. С точки зрения приложения метод расширения и обычные методы экземпляров одинаковы.

Дополнительные сведения о методах расширения см. в разделе Методы расширения. Дополнительные сведения о стандартных операторах запросов см. в разделе Общие сведения о стандартных операторах запроса (C#). Некоторые поставщики LINQ, такие как Entity Framework и LINQ to XML, реализуют собственные стандартные операторы запросов и методы расширения для других типов, кроме IEnumerable<T>.

Лямбда-выражения

В предыдущем примере обратите внимание, что условное выражение (num % 2 == 0) передается как встроенный аргумент Enumerable.Where методу: Where(num => num % 2 == 0). это встроенное выражение является лямбда-выражением. Это удобный способ написания кода, который в противном случае должен быть написан в более сложной форме. num Слева от оператора — входная переменная, соответствующая num выражению запроса. Компилятор может вывести тип num, поскольку известно, что numbers является универсальным типом IEnumerable<T>. Текст лямбда-выражения совпадает с выражением в синтаксисе запроса или в любом другом выражении или операторе C#. Он может включать вызовы методов и другую сложную логику. Возвращаемое значение — это только результат выражения. Некоторые запросы можно выразить только в синтаксисе метода, а некоторые из них требуют лямбда-выражений. Лямбда-выражения — это мощный и гибкий инструмент на панели элементов LINQ.

Возможность создания запросов

В предыдущем примере Enumerable.OrderBy кода метод вызывается с помощью оператора dot в вызове Where. Where создает отфильтрованную последовательность, а затем Orderby сортирует последовательность, созданную Where. Поскольку запросы возвращают IEnumerable, объедините их в синтаксис метода, собрав вызовы методов в цепочку. Компилятор выполняет эту композицию при написании запросов с помощью синтаксиса запросов. Так как переменная запроса не хранит результаты запроса, ее можно изменить или использовать в качестве основы для нового запроса в любое время, даже после его выполнения.

Следующие примеры демонстрируют некоторые простые запросы LINQ, составленные с использованием каждого из указанных выше методов.

Примечание.

Эти запросы обращаются к простым коллекциям в памяти, однако базовый синтаксис при этом точно такой же, как в запросах LINQ to Entities и LINQ to XML.

Пример синтаксиса запросов

Большинство запросов записываются с синтаксисом запросов для создания выражений запросов. В следующем примере показаны три выражения запросов. Первое выражение запроса показывает, как фильтровать или ограничивать результаты, применяя условия в предложении where. Оно возвращает все элементы исходной последовательности, значения которых больше 7 или меньше 3. Второе выражение показывает, как сортировать возвращаемые результаты. Третье выражение показывает, как группировать результаты по ключу. Этот запрос возвращает две группы на основе первой буквы слова.

List<int> numbers = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];

// The query variables can also be implicitly typed by using var

// Query #1.
IEnumerable<int> filteringQuery =
    from num in numbers
    where num is < 3 or > 7
    select num;

// Query #2.
IEnumerable<int> orderingQuery =
    from num in numbers
    where num is < 3 or > 7
    orderby num ascending
    select num;

// Query #3.
string[] groupingQuery = ["carrots", "cabbage", "broccoli", "beans", "barley"];
IEnumerable<IGrouping<char, string>> queryFoodGroups =
    from item in groupingQuery
    group item by item[0];

Тип запросов .IEnumerable<T> Все эти запросы можно написать с помощью var, как показано в следующем примере:

var query = from num in numbers...

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

Пример синтаксиса метода

Некоторые операции запросов должны быть выражены как вызов метода. Наиболее распространенными такими методами являются те методы, которые возвращают однотонные числовые значения, такие как Sum, Max, Minи Averageт. д. Эти методы всегда должны вызываться последним в любом запросе, так как они возвращают одно значение и не могут служить источником для дополнительной операции запроса. В следующем примере показан вызов метода в выражении запроса:

List<int> numbers1 = [5, 4, 1, 3, 9, 8, 6, 7, 2, 0];
List<int> numbers2 = [15, 14, 11, 13, 19, 18, 16, 17, 12, 10];

// Query #4.
double average = numbers1.Average();

// Query #5.
IEnumerable<int> concatenationQuery = numbers1.Concat(numbers2);

Если метод имеет System.Action или System.Func<TResult> параметры, эти аргументы предоставляются в виде лямбда-выражения, как показано в следующем примере:

// Query #6.
IEnumerable<int> largeNumbersQuery = numbers2.Where(c => c > 15);

В предыдущих запросах выполняется только запрос #4, так как он возвращает одно значение, а не универсальную IEnumerable<T> коллекцию. Сам метод использует foreach или аналогичный код для вычисления значения.

Каждый из предыдущих запросов можно записать с помощью неявного ввода с помощью var, как показано в следующем примере:

// var is used for convenience in these queries
double average = numbers1.Average();
var concatenationQuery = numbers1.Concat(numbers2);
var largeNumbersQuery = numbers2.Where(c => c > 15);

Пример смешанного синтаксиса запроса и метода

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

// Query #7.

// Using a query expression with method syntax
var numCount1 = (
    from num in numbers1
    where num is > 3 and < 7
    select num
).Count();

// Better: Create a new variable to store
// the method call result
IEnumerable<int> numbersQuery =
    from num in numbers1
    where num is > 3 and < 7
    select num;

var numCount2 = numbersQuery.Count();

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

Предыдущий запрос можно написать, используя неявную типизацию с переменной var, как показано ниже:

var numCount = (from num in numbers...

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

var numCount = numbers.Count(n => n is > 3 and < 7);

При записи с явной типизацией он будет выглядеть следующим образом:

int numCount = numbers.Count(n => n is > 3 and < 7);

См. также