Запросы в LINQ to Entities
Запрос представляет собой выражение, получающее данные из источника данных. Запросы обычно выражаются на специализированном языке запросов, например, SQL — для реляционных баз данных и XQuery — для XML. Поэтому разработчикам приходится учить новый язык запросов для каждого типа источника данных и формата данных, для которых выполняется запрос. Интегрированный в язык запрос (LINQ) предлагает упрощенную согласованную модель работы с данными для различных типов источников данных и различных форматов данных. Запросы LINQ всегда работают с программируемыми объектами.
Операция запроса LINQ состоит из трех действий: получение одного или нескольких источников данных, создание запроса и выполнение запроса.
К источникам данных, реализующим общий интерфейс IEnumerable или IQueryable, можно выполнять запрос с помощью LINQ. Экземпляры универсального класса ObjectQuery, реализующего общий интерфейс IQueryable, служат источником данных для запросов LINQ to Entities. Универсальный класс ObjectQuery представляет запрос, который возвращает экземпляр или коллекцию типизированных сущностей. Разработчик создает экземпляр, производный от класса ObjectContext, являющегося базовым классом для взаимодействия с моделью Entity Data Model (EDM) в качестве объектов CLR.
В запросе указываются данные, которые необходимо получить из источника данных. В запросе можно также указать, как следует сортировать, группировать и формировать возвращаемую информацию. В LINQ запрос хранится в переменной. Если запрос вернул последовательность значений, то переменная запроса должна иметь тип данных, которые можно запросить. Эта переменная не выполняет никаких действий и не возвращает данные. Она только хранит информацию о запросе. После создания запроса его необходимо выполнить для получения данных.
В запросе, возвращающем последовательность значений, переменная запроса никогда не содержит результаты запроса, а только хранит его команды. Выполнение запроса откладывается, пока переменная запроса не будет обработана в цикле foreach или For Each. Это называется отложенным выполнением, то есть выполнение запроса происходит спустя некоторое время после его создания. Это означает, что запрос можно выполнять настолько часто, насколько это необходимо. Такое свойство полезно, например, если имеется база данных, которая обновляется другими приложениями. В собственном приложении можно создать запрос, который регулярно выполняется, каждый раз получая последние обновленные данные.
В отличие от отложенных запросов, возвращающих последовательности значений, запросы, получающие одноэлементное значение, выполняются немедленно. Примерами одноэлементных запросов являются Count, Max, Average и First. Они выполняются немедленно, потому что результаты запроса необходимы для вычисления одноэлементного результата. Для принудительного немедленного выполнения запроса, не создающего одноэлементное значение, можно также использовать метод ToList или ToArray. Такая методика принудительного немедленного выполнения может оказаться полезной при кэшировании результатов запроса. Дополнительные сведения об отложенном и немедленном выполнении запросов см. в разделе Introduction to LINQ.
Запросы
При составлении запросов LINQ to Entities можно использовать два разных синтаксиса: синтаксис выражений запросов и синтаксис запросов на основе методов. Синтаксис выражений запросов является новой возможностью в C# версии 3.0 и Visual Basic версии 9.0. Он состоит из набора предложений, использующих декларативный синтаксис, аналогичный Transact-SQL и XQuery. Но сама среда CLR платформы .NET Framework не может распознать синтаксис выражения запроса. Поэтому во время компиляции выражения запроса преобразуются в формат, понятный среде CLR: вызовы методов. Эти методы называются стандартными операторами запросов. Разработчик имеет возможность вызывать их напрямую, используя синтаксис методов вместо синтаксиса запроса. Дополнительные сведения см. в разделе LINQ Query Syntax versus Method Syntax (C#). Дополнительные сведения об использовании стандартных операторов запроса см. в разделе LINQ General Programming Guide.
Синтаксис выражений запросов
Выражения запроса используют декларативный синтаксис запроса. Этот синтаксис позволяет разработчикам писать запросы на языке высокого уровня, напоминающем Transact-SQL. С помощью синтаксиса выражения запроса можно выполнять даже сложную фильтрацию, упорядочение и группирование операций в источнике данных с помощью минимального программного кода. Дополнительные сведения см. в разделах Выражения запросов LINQ (Руководство по программированию на C#) и Basic Query Operations (Visual Basic).
В следующем примере показано использование метода Select для выборки всех строк из таблицы Product и вывода названий продуктов.
Using AWEntities As New AdventureWorksEntities
Dim products As ObjectQuery(Of Product) = AWEntities.Product
Dim productNames = _
From p In products _
Select p.Name
Console.WriteLine("Product Names:")
For Each productName In productNames
Console.WriteLine(productName)
Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<string> productNames =
from p in products
select p.Name;
Console.WriteLine("Product Names:");
foreach (var productName in productNames)
{
Console.WriteLine(productName);
}
}
Синтаксис запросов на основе методов
Другим способом создания запросов LINQ to Entities является синтаксис запроса на основе методов. Он представляет собой последовательность непосредственных вызовов методов операторов LINQ, передающих лямбда-выражения в качестве параметров. Дополнительные сведения см. в разделе Лямбда-выражения (Руководство по программированию на C#).
В следующем примере выражение Select используется для возврата всех строк из таблицы Product и вывода названий продуктов.
Using AWEntities As New AdventureWorksEntities
Dim productNames = AWEntities.Product.Select(Function(p) p.Name)
Console.WriteLine("Product Names:")
For Each productName In productNames
Console.WriteLine(productName)
Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<string> productNames = products.Select(p => p.Name);
Console.WriteLine("Product Names:");
foreach (var productName in productNames)
{
Console.WriteLine(productName);
}
}
Создание запросов
Как указывалось выше в этом разделе, переменная запроса хранит команды запроса, если он предназначен для возврата последовательности значений. Если запрос не содержит метод, приводящий к немедленному выполнению запроса, фактическое выполнение запроса откладывается до завершения обработки переменной запроса в цикле foreach или For Each. Отложенное выполнение позволяет сочетать несколько запросов или расширять один запрос. При расширении запроса он изменяется, включая в свой состав новые операции, а последующее выполнение отразит эти изменения. В следующем примере первый запрос возвращает все продукты. Второй запрос расширяет первый, используя предложение Where, чтобы возвратить все продукты с размером «L»:
Using AWEntities As New AdventureWorksEntities()
Dim products As ObjectQuery(Of Product) = AWEntities.Product
Dim productsQuery = _
From p In products _
Select p
Dim largeProducts = _
productsQuery.Where(Function(p) p.Size = "L")
Console.WriteLine("Products of size 'L':")
For Each product In largeProducts
Console.WriteLine(product.Name)
Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<Product> productsQuery =
from p in products
select p;
IQueryable<Product> largeProducts = productsQuery.Where(p => p.Size == "L");
Console.WriteLine("Products of size 'L':");
foreach (var product in largeProducts)
{
Console.WriteLine(product.Name);
}
}
После выполнения запроса все последующие запросы будут пользоваться операторами LINQ, сохраненными в памяти. Перебор по переменной запроса в цикле foreach или For Each либо вызов одного из операторов преобразования LINQ приведет к немедленному выполнению. К этим операторам преобразования относятся: ToList, ToArray, ToLookup и ToDictionary.
В следующем примере первый запрос возвращает все продукты. Второй запрос расширяет первый запрос после его выполнения, чтобы вернуть товары красного цвета.
Using AWEntities As New AdventureWorksEntities()
Dim products As ObjectQuery(Of Product) = AWEntities.Product
Dim productsQuery = _
From p In products _
Select p
Console.WriteLine("The list of products:")
For Each Product As Product In productsQuery
Console.WriteLine(Product.Name)
Next
Dim redProducts = productsQuery _
.Where(Function(p) p.Color = "Red") _
.Select(Function(p) p)
Console.WriteLine("")
Console.WriteLine("The list of red products:")
For Each redProduct As Product In redProducts
Console.WriteLine(redProduct.Name)
Next
End Using
using (AdventureWorksEntities AWEntities = new AdventureWorksEntities())
{
ObjectQuery<Product> products = AWEntities.Product;
IQueryable<Product> productsQuery =
from p in products
select p;
Console.WriteLine("The list of products:");
foreach (Product product in productsQuery)
{
Console.WriteLine(product.Name);
}
IQueryable<Product> redProducts = productsQuery
.Where(p => p.Color == "Red")
.Select(p => p);
Console.WriteLine("");
Console.WriteLine("The list of red products:");
foreach (Product redProduct in redProducts)
{
Console.WriteLine(redProduct.Name);
}
}
См. также
Другие ресурсы
Технология LINQ to Entities
Getting Started with LINQ in C#
Getting Started with LINQ in Visual Basic