Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
После создания пользователем запроса LINQ он преобразуется в дерево команд. Дерево команд — это представление запроса, совместимого с Entity Framework. Затем дерево команд выполняется в источнике данных. Во время выполнения запроса вычисляются все выражения запросов (т. е. все компоненты запроса), включая те выражения, которые используются в материализации результатов.
Момент выполнения выражений запросов может изменяться. Запросы LINQ всегда выполняются при итерации переменной запроса, а не при создании переменной запроса. Это называется отложенным выполнением. Вы также можете принудительно выполнить запрос немедленно, что полезно для кэширования результатов запроса. Это описано далее в этом разделе.
При выполнении запроса LINQ to Entities некоторые выражения в запросе могут выполняться на сервере, а некоторые части могут выполняться локально на клиенте. Клиентское вычисление выражения происходит перед выполнением запроса на сервере. Если выражение вычисляется на клиенте, результат этой оценки заменяет выражение в запросе, а затем запрос выполняется на сервере. Так как запросы выполняются в источнике данных, конфигурация источника данных переопределяет поведение, указанное в клиенте. Например, обработка значений NULL и числовая точность зависят от параметров сервера. Все исключения, возникающие во время выполнения запроса на сервере, передаются непосредственно клиенту.
Подсказка
Удобная сводка операторов запросов в формате таблицы, которая позволяет быстро определить поведение выполнения оператора, см. в разделе Классификация стандартных операторов запросов по способу выполнения (C#).
Отложенное выполнение запроса
В запросе, который возвращает последовательность значений, переменная запроса никогда не содержит результаты запроса и сохраняет только команды запроса. Выполнение запроса откладывается до тех пор, пока переменная запроса не будет итерирована в цикле foreach или For Each. Это называется отложенным выполнением; То есть выполнение запроса происходит некоторое время после создания запроса. Это означает, что вы можете выполнять запрос так часто, как вы хотите. Это полезно, например, если у вас есть база данных, обновляемая другими приложениями. В приложении можно создать запрос для получения последних сведений и многократного выполнения запроса, возвращая обновленные сведения каждый раз.
Отложенное выполнение позволяет объединить несколько запросов или расширить запрос. Если запрос расширен, он изменяется, чтобы включить новые операции, и в конечном итоге выполнение будет отражать изменения. В следующем примере первый запрос возвращает все продукты. Второй запрос расширяет первый с помощью Where для возврата всех продуктов размера "L":
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
IQueryable<Product> productsQuery =
from p in context.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);
}
}
Using context As New AdventureWorksEntities()
Dim productsQuery = _
From p In context.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
После выполнения запроса все последовательные запросы будут использовать операторы LINQ в памяти. Итерация по переменной запроса с помощью инструкции foreach или вызова одного из операторов преобразования LINQ приведет к немедленному выполнению. Эти операторы преобразования включают следующие: ToList, ToArray, ToLookupи ToDictionary.
Немедленное выполнение запроса
В отличие от отложенного выполнения запросов, которые создают последовательность значений, запросы, возвращающие однотонное значение, выполняются немедленно. Ниже приведены некоторые примеры одноэлементных запросов: Average, Count, Firstи Max. Они выполняются немедленно, так как запрос должен создать последовательность для вычисления одноэлементного результата. Вы также можете принудительно выполнить команду немедленно. Это полезно, если требуется кэшировать результаты запроса. Чтобы принудительно выполнить запрос или переменную запроса, которые не создают одиночное значение, можно вызвать метод ToList, метод ToDictionary или метод ToArray. В следующем примере метод ToArray используется для немедленной оценки последовательности в массиве.
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
ObjectSet<Product> products = context.Products;
Product[] prodArray = (
from product in products
orderby product.ListPrice descending
select product).ToArray();
Console.WriteLine("Every price from highest to lowest:");
foreach (Product product in prodArray)
{
Console.WriteLine(product.ListPrice);
}
}
Using context As New AdventureWorksEntities
Dim products As ObjectSet(Of Product) = context.Products
Dim prodArray As Product() = ( _
From product In products _
Order By product.ListPrice Descending _
Select product).ToArray()
Console.WriteLine("The list price from highest to lowest:")
For Each prod As Product In prodArray
Console.WriteLine(prod.ListPrice)
Next
End Using
Вы также можете принудительно выполнить выполнение, поместив foreach или For Each сразу после выражения запроса, но вызвав ToList или ToArray, вы кэшируете все данные в одном объекте коллекции.
Управление магазином
Как правило, выражения в LINQ to Entities оцениваются на сервере, и поведение выражения не должно соответствовать семантике общеязыковой среды CLR, а семантике источника данных. Однако существуют исключения, например, когда выражение выполняется на клиенте. Это может привести к непредвиденным результатам, например, если сервер и клиент находятся в разных часовых поясах.
Некоторые выражения в запросе могут выполняться на клиенте. Как правило, на сервере ожидается выполнение большинства запросов. Помимо методов, выполняемых против элементов запроса, сопоставленных с источником данных, часто встречаются выражения, которые могут выполняться локально в запросе. Локальное выполнение выражения запроса дает значение, которое можно использовать в выполнении запроса или построении результатов.
Некоторые операции всегда выполняются на клиенте, например привязка значений, подвыражений, подзапросов из замыканий и превращение объектов в результаты запроса. Чистый эффект этого заключается в том, что эти элементы (например, значения параметров) нельзя обновить во время выполнения. Анонимные типы могут быть созданы непосредственно в источнике данных, но не следует считать, что это всегда так. Встроенные группировки можно создать непосредственно в источнике данных, хотя не следует предполагать это в каждом случае. В общем, лучше не делать никаких предположений о том, что построено на сервере.
В этом разделе описываются сценарии, в которых код выполняется локально на клиенте. Дополнительные сведения о том, какие типы выражений выполняются локально, см. в разделе "Выражения" в запросах LINQ to Entity.
Литералы и параметры
Локальные переменные, такие как orderID переменная в следующем примере, оцениваются на клиенте.
int orderID = 51987;
IQueryable<SalesOrderHeader> salesInfo =
from s in context.SalesOrderHeaders
where s.SalesOrderID == orderID
select s;
Dim orderID As Integer = 51987
Dim salesInfo = _
From s In context.SalesOrderHeaders _
Where s.SalesOrderID = orderID _
Select s
Параметры метода также вычисляются на клиенте. Параметр orderID , переданный в MethodParameterExample метод ниже, является примером.
public static void MethodParameterExample(int orderID)
{
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
IQueryable<SalesOrderHeader> salesInfo =
from s in context.SalesOrderHeaders
where s.SalesOrderID == orderID
select s;
foreach (SalesOrderHeader sale in salesInfo)
{
Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue);
}
}
}
Function MethodParameterExample(ByVal orderID As Integer)
Using context As New AdventureWorksEntities()
Dim salesInfo = _
From s In context.SalesOrderHeaders _
Where s.SalesOrderID = orderID _
Select s
Console.WriteLine("Sales order info:")
For Each sale As SalesOrderHeader In salesInfo
Console.WriteLine("OrderID: {0}, Total due: {1}", sale.SalesOrderID, sale.TotalDue)
Next
End Using
End Function
Преобразование литералов на стороне клиента
Приведение от null к типу общего языка выполнения CLR выполняется на клиенте.
IQueryable<Contact> query =
from c in context.Contacts
where c.EmailAddress == (string)null
select c;
Dim query = _
From c In context.Contacts _
Where c.EmailAddress = CType(Nothing, String) _
Select c
Приведение к типу, например null, Decimalвыполняется на клиенте:
var weight = (decimal?)23.77;
IQueryable<Product> query =
from product in context.Products
where product.Weight == weight
select product;
Dim weight = CType(23.77, Decimal?)
Dim query = _
From product In context.Products _
Where product.Weight = weight _
Select product
Конструкторы для литералов
Новые типы CLR, которые можно сопоставить с концептуальными типами модели, выполняются на клиенте.
var weight = new decimal(23.77);
IQueryable<Product> query =
from product in context.Products
where product.Weight == weight
select product;
Dim weight = New Decimal(23.77)
Dim query = _
From product In context.Products _
Where product.Weight = weight _
Select product
Новые массивы также выполняются на клиенте.
Сохранение исключений
Все ошибки хранилища, возникающие во время выполнения запроса, передаются клиенту и не сопоставляются или не обрабатываются.
Конфигурация хранилища
Когда запрос выполняется в хранилище, конфигурация хранилища переопределяет все поведение клиента, а семантика хранилища выражается для всех операций и выражений. Это может привести к разнице в поведении между средой CLR и выполнением в хранилище в таких областях, как сравнение с NULL, упорядочение GUID, точность и аккуратность операций, связанных с не точными типами данных (например, плавающими типами данных или DateTime), и строковыми операциями. Важно помнить об этом при проверке результатов запроса.
Например, ниже приведены некоторые различия в поведении среды CLR и SQL Server:
SQL Server упорядочивает GUID иначе, чем CLR.
При работе с десятичным типом в SQL Server также могут быть различия в точности результатов. Это связано с фиксированными требованиями точности десятичного типа SQL Server. Например, среднее Decimal значение 0.0, 0.0 и 1.0 равно 0,333333333333333333333333333333 в памяти клиента, но 0,3333333 в хранилище (на основе точности десятичного типа SQL Server по умолчанию).
Некоторые операции сравнения строк также обрабатываются по-разному в SQL Server, чем в среде CLR. Поведение сравнения строк зависит от параметров сортировки на сервере.
Вызовы функций или методов, включенные в запрос LINQ to Entity, сопоставляются с каноническими функциями в Entity Framework, которые затем переводятся в Transact-SQL и выполняются в базе данных SQL Server. Существуют случаи, когда поведение этих сопоставленных функций может отличаться от реализации в библиотеках базовых классов. Например, вызов методов Contains, StartsWith и EndsWith с пустой строкой в качестве параметра вернет
true, если выполняется в среде CLR, но вернетfalse, если выполняется в SQL Server. Метод EndsWith также может возвращать разные результаты, так как SQL Server считает две строки равными, если они отличаются только в конечных пробелах, в то время как среда CLR считает, что они не равны. Это иллюстрируется следующим примером:
using (AdventureWorksEntities context = new AdventureWorksEntities())
{
IQueryable<string> query = from p in context.Products
where p.Name == "Reflector"
select p.Name;
IEnumerable<bool> q = query.Select(c => c.EndsWith("Reflector "));
Console.WriteLine("LINQ to Entities returns: " + q.First());
Console.WriteLine("CLR returns: " + "Reflector".EndsWith("Reflector "));
}
Using context As New AdventureWorksEntities()
Dim query = _
From p In context.Products _
Where p.Name = "Reflector" _
Select p.Name
Dim q = _
query.Select(Function(c) c.EndsWith("Reflector "))
Console.WriteLine("LINQ to Entities returns: " & q.First())
Console.WriteLine("CLR returns: " & "Reflector".EndsWith("Reflector "))
End Using