Cоздание запросов
Entity Framework предоставляет два основных типа запросов: LINQ To Entities и Entity SQL (eSQL). При этом если Object Services поддерживает оба типа запросов, то Entity Client поддерживает лишь eSQL. Да и вообще, Object Services является основным средством создания запросов, а к Entity Client, как правило, прибегают лишь в крайних случаях.LINQ To Entities представляет собой реализацию LINQ (в статье подразумевается, что читатель знаком с основами LINQ) для работы с Entity Framework, а следовательно поддерживает два типа синтаксиса: query-syntax и method-base syntax (эти понятия буду приводить на английском языке: уж больно нелаконично они звучат на русском).
Entity SQL создавался как основное средство создания запросов. Однако c появлением LINQ стало очевидно, что при решении большинства задач его использование оказывается гораздо более удобным (к слову, разработчики NHibernate после релиза LINQ создали свою реализацию LINQ - LINQ To NHibernate). Синтаксис eSQL схож с синтаксисом SQL (хотя еще больше он похож на синтаксис hSQL ;) ), однако между ними существуют принципиальные различия (помимо того, что eSQL -язык запросов к модели, а SQL - язык запросов к реляционным БД): так, eSQL, в отличие от SQL, поддерживает наследование и навигационные свойства.
Object Services
Начнем с Lint To Entities.1. Query-syntax.
Результат выполнения запроса:
using (FirstModel ctx = new FirstModel())
{
var persons = from p in ctx.Persons
where p.Address.City == "Tomsk"
select p;
Console.WriteLine("В Томске проживают:");
foreach (Person person in persons)
Console.WriteLine(person.Name + " " + person.Surname);
Console.ReadLine();
}
В Томске проживают:Мы написали запрос, который возвращает всех Person, проживающих в городе Томске (этот запрос мы будем использовать для всех примеров).
Evgenii Salomatov
Andrei Lupanov
Vitalii Pogonin
Evgenii Hudoba
2. Method-based syntax.
Перепишем запрос:
Перейдем к eSQL-запросам.
using (FirstModel ctx = new FirstModel())
{
var persons = ctx.Persons.Where(p => p.Address.City == "Tomsk");
Console.WriteLine("В Томске проживают:");
foreach (Person person in persons)
Console.WriteLine(person.Name + " " + person.Surname);
Console.ReadLine();
}
1. Чистый eSQL.
В ObjectContext есть метод CreateQuery
2. Query-builder методы.
using (FirstModel ctx = new FirstModel())
{
string queryString = "SELECT VALUE p FROM FirstModel.Persons AS p WHERE p.Address.City ='Tomsk'";
var persons = ctx.CreateQuery<Person>(queryString);
Console.WriteLine("В Томске проживают:");
foreach (Person person in persons)
Console.WriteLine(person.Name + " " + person.Surname);
Console.ReadLine();
}
Некоторые методы (Where, Select и др.), используемые в method-based LINQ To Entities
Перепишем наш method-based запрос с применением query-builder метода:
Данный способ приходится очень кстати, когда нужно добавить в запрос немного динамики (например, сортировку по полям, выбранным пользователем), но при этом не хочется терять строгую типизацию, переходя на чистый eSQL-запрос.
using (FirstModel ctx = new FirstModel())
{
var persons = ctx.Persons.Where("it.Address.City == 'Tomsk'");
Console.WriteLine("В Томске проживают:");
foreach (Person person in persons)
Console.WriteLine(person.Name + " " + person.Surname);
Console.ReadLine();
}
В запросе используется алиас по умолчанию - "it". Его можно изменить, воспользовавшись свойством Name класс ObjectQuery
Подведем промежуточные итоги.
var persons = ctx.Persons;
persons.Name = "p";
persons = persons.Where("p.Address.City == 'Tomsk'");
1. Когда использовать чистые eSQL-запросы? Тогда, когда запрос формируется динамически, и query-builder методов не достаточно.
2. Что выбрать: query или method-based LINQ To Entities? Здесь многое зависит от личных предпочтений (для кого-то выразительность query-синтаксиса может оказаться дороже всех преимущества method-based синтаксиса)... но я рекомендую использовать method-based синтаксис: во-первых, при помощи query-синтаксиса можно выразить далеко не весь функционал LINQ (и LINQ To Entities в частности), а, во-вторых, из 13 query-builder методов query-синтаксис поддерживает лишь один - Include.
Если, читая статью, Вы успели соскучиться по старому-доброму ADO.NET, у меня для Вас приятный сюрприз. EntityClient, являясь более низкоуровневым компонентом, чем Object Services (как мы выяснили во второй статье, Object Service считывает данных при помощи EntityClient, а затем материализует их), оперирует умными наследниками таких базовых классов ADO.NET, как DbConnection, DbCommand, DbDataReader и т. д. Почему умными? Потому что в них учитывается специфика Entity Framewok: так, например, EntityDataReader поддерживает не только скалярные значения, но и DbDataReader, DbDataRecord и EntityKey.
Отмечу, что EntityClient не является самым низкоуровневым средством создания запросов, так как EDM поддерживает запросы на нативном SQL (при помощи Defining Query).
Перепишем наш запрос с использованием EntityClient:
Напоследок я раскрою третий аргумент в противостоянии method-based vs. query syntax.
Отмечу, что EntityClient не является самым низкоуровневым средством создания запросов, так как EDM поддерживает запросы на нативном SQL (при помощи Defining Query).
Перепишем наш запрос с использованием EntityClient:
Вот мы и рассмотрели все типы запросов в EF (кроме DefiningQuery... но это тема для отдельного разговора).
using (EntityConnection connection = new EntityConnection("name=FirstModel"))
{
string queryString = "SELECT VALUE p FROM FirstModel.Persons AS p WHERE p.Address.City ='Tomsk'";
EntityCommand command = connection.CreateCommand();
command.CommandText = queryString;
connection.Open();
Console.WriteLine("В Томске проживают:");
using (EntityDataReader dataReader = command.ExecuteReader(CommandBehavior.SequentialAccess))
{
while (dataReader.Read())
{
string firstName = dataReader.GetString(1);
string secondName = dataReader.GetString(2);
Console.WriteLine(firstName + " " + secondName);
}
}
Console.ReadLine();
}
Напоследок я раскрою третий аргумент в противостоянии method-based vs. query syntax.
ObjectQuery vs. IQueryable
ObjectQuery - это класс, который содержит все необходимую информацию о EF-запросе. Он реализует ряд интерфейсов, в том числе и IQueryable. Некоторые Linq To Entities методы возвращают ObjectQuery, а некоторые - IQueryable:
Самая серьезная потеря в IQueryable по сравнению с ObjectQuery - метод Execute (который мы рассмотрим в начале следующей статьи). Однако потерю можно восполнить: на самом деле эти методы возвращают не IQueryable, а ObjectQuery, поэтому нам нужно лишь привести тип:
Заключение
using (FirstModel ctx = new FirstModel())
{
var iQueryablePersons = from p in ctx.Persons
where p.Address.City == "Tomsk"
select p;
var objectQueryPersons = (ObjectQuery)iQueryablePersons;
objectQueryPersons.Execute(MergeOption.AppendOnly);
Console.WriteLine("В Томске проживают:");
foreach (Person person in objectQueryPersons)
Console.WriteLine(person.Name + " " + person.Surname);
Console.ReadLine();
}
К этому моменту мы изучили подноготную запросов (шаг 3) и типы запросов (эта статья). В следующий раз (боюсь, это случится не очень скоро) мы подробнее остановимся на процессе выполнения запросов и маппинге.