Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM Новый топик    Ответить
 Негерация запросов в EF 4.0  [new]
Dmitry Sukhovilin
Member

Откуда: Таганрог
Сообщений: 363
Добрый день,

Пробую возможности Entiry Framework 4.0 (Visual Studio 2010 RC). До этого работал с Hibernate 2.1.

И так...
Есть маппинг (см рисунок). В БД Таблица Managers нстедуется от Persons.
делаю запрос в C#

Domain.TestDBEntities c = new Domain.TestDBEntities();
foreach (var p in c.Persons)
{
	Console.WriteLine(p.FirstName + " " + p.LastName);
}

Смотрю в профйлер:

SELECT 
CASE WHEN ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL))) THEN '0X' ELSE '0X0X' END AS [C1], 
[Extent1].[PersonId] AS [PersonId], 
[Extent1].[Version] AS [Version], 
[Extent1].[FirstName] AS [FirstName], 
[Extent1].[LastName] AS [LastName], 
CASE WHEN ( NOT (([Project1].[C1] = 1) AND ([Project1].[C1] IS NOT NULL))) THEN CAST(NULL AS int) ELSE [Project1].[EmployeeNumber] END AS [C2]
FROM  [dbo].[Persons] AS [Extent1]
LEFT OUTER JOIN  (SELECT 
	[Extent2].[PersonId] AS [PersonId], 
	[Extent2].[EmployeeNumber] AS [EmployeeNumber], 
	cast(1 as bit) AS [C1]
	FROM [dbo].[Managers] AS [Extent2] ) AS [Project1] ON [Extent1].[PersonId] = [Project1].[PersonId]

В запросе используется таблица Managers, которая мне не нужна !!!
В Execution plan видно, что cost ображения в Managers 51%. Т.е. это БОЛЬШЕ ПОЛОВИНЫ !
Кроме того,

Domain.TestDBEntities c = new Domain.TestDBEntities();

foreach (var p in c.Persons.OfType<Domain.Person>())
{
	Console.WriteLine(p.FirstName + " " + p.LastName);
}

не изменяет запрос. Хотя в описании OfType написано
// Summary:
// Limits the query to only results of a specific type.

Плиз, побскажите, где я ошибся....

Спасибо.

К сообщению приложен файл. Размер - 0Kb
16 фев 10, 11:31    [8350524]     Ответить | Цитировать Сообщить модератору
 Re: Негерация запросов в EF 4.0  [new]
Dmitry Sukhovilin
Member

Откуда: Таганрог
Сообщений: 363
А вот и план

К сообщению приложен файл. Размер - 0Kb
16 фев 10, 11:33    [8350538]     Ответить | Цитировать Сообщить модератору
 Re: Негерация запросов в EF 4.0  [new]
Dmitry Sukhovilin
Member

Откуда: Таганрог
Сообщений: 363
Even more,
var query = from p in c.Persons
select p;

Всеравно делает обращение в Managers
16 фев 10, 11:44    [8350624]     Ответить | Цитировать Сообщить модератору
 Re: Негерация запросов в EF 4.0  [new]
МихаилР
Member

Откуда: Ижевск
Сообщений: 255
Ну, то, что при простом обращении
c.Persons
он подтягивает Managers - вполне понятно и логично, чтобы инстанцировать конкретный объект ему нужна информация из обеих таблиц - как иначе узнать заранее будет ли конкретный объект просто персоной или уже менеджером?
Вариантов 2:
- или вытаскивать все, что в принципе может пригодится, 1 запросом.
- либо достать ведущую таблицу, определить тип каждого элемента и для тех, кто не "не просто персона" - достать еще дополнительные поля (этакая Lazy-инициализация).
В большинстве случаев первый вариант предпочтительнее. По крайней мере, на сколько я помню, в NH реализация аналогичная.

Почему не срабатывает
c.Persons.OfType<Domain.Person>()
у меня тоже есть объяснение.

Собственно, в описании метода OfType есть такая ремарка:
A new ObjectQuery(T) instance that is equivalent to the original instance with OFTYPE applied.

Соответсвенно, если посмотреть на описание оператора OFTYPE, то можно увидеть что он возвращает все объекты приводимые (т.е. унаследованные от ...) к указанному типу, кроме случая, когда указана опция ONLY.

В принципе, это поведение полностью соответствует семантике для OfType, которое описано в статье The .NET Standard Query Operators, а именно:
The OfType operator filters the elements of a sequence based on a type.

Думаю, в формальной спецификации C# будет описано аналогичное поведение - в выборку попадает как базовый, так и наследуемые типы.
17 фев 10, 10:07    [8355535]     Ответить | Цитировать Сообщить модератору
 Re: Негерация запросов в EF 4.0  [new]
Dmitry Sukhovilin
Member

Откуда: Таганрог
Сообщений: 363
МихаилР
Ну, то, что при простом обращении
c.Persons
он подтягивает Managers - вполне понятно и логично, чтобы инстанцировать конкретный объект ему нужна информация из обеих таблиц - как иначе узнать заранее будет ли конкретный объект просто персоной или уже менеджером?

А это не имеет значения, т.к. менеджер это тоже персона. Если я говорю - хочу список персон, то мне не важно какой тип у персоны, менеджер, стедент, и т.д. Мне нужен базовый тип, список всех персон.

МихаилР

Вариантов 2:
- или вытаскивать все, что в принципе может пригодится, 1 запросом.
- либо достать ведущую таблицу, определить тип каждого элемента и для тех, кто не "не просто персона" - достать еще дополнительные поля (этакая Lazy-инициализация).
В большинстве случаев первый вариант предпочтительнее. По крайней мере, на сколько я помню, в NH реализация аналогичная.


1) Не желательно, вполне возможно, что большенство данных не понадобятся.
2) Это порождает ображение к другим таблицам.

NHibernate дает ровно столько, сколько от него просишь дать. При этом SQL получается лучше.

МихаилР

Почему не срабатывает
c.Persons.OfType<Domain.Person>()
у меня тоже есть объяснение.

Собственно, в описании метода OfType есть такая ремарка:
A new ObjectQuery(T) instance that is equivalent to the original instance with OFTYPE applied.

Соответсвенно, если посмотреть на описание оператора OFTYPE, то можно увидеть что он возвращает все объекты приводимые (т.е. унаследованные от ...) к указанному типу, кроме случая, когда указана опция ONLY.

В принципе, это поведение полностью соответствует семантике для OfType, которое описано в статье The .NET Standard Query Operators, а именно:
The OfType operator filters the elements of a sequence based on a type.

Думаю, в формальной спецификации C# будет описано аналогичное поведение - в выборку попадает как базовый, так и наследуемые типы.


Спасибо за подробный ответ.
17 фев 10, 11:04    [8355975]     Ответить | Цитировать Сообщить модератору
 Re: Негерация запросов в EF 4.0  [new]
Dmitry Sukhovilin
Member

Откуда: Таганрог
Сообщений: 363
Я нашел способ получения правильного запроса, т.е. только по таблице Persons.
17 фев 10, 11:28    [8356172]     Ответить | Цитировать Сообщить модератору
 Re: Негерация запросов в EF 4.0  [new]
МихаилР
Member

Откуда: Ижевск
Сообщений: 255
автор
А это не имеет значения, т.к. менеджер это тоже персона.


Именно!
Хорошо, вот смотрите простейший пример (модификация вашего):
Domain.TestDBEntities c = new Domain.TestDBEntities();
foreach (var p in c.Persons)
{
   Console.Write(p.FirstName + " " + p.LastName + " ");
   Domain.Manager m = p as Domain.Manager;
   if (m != null)
   {
       Console.Write("это менеджер с " + m.EmployeeNumber + " подчиненных");
   }
   Console.WriteLine();
}

Как вы думете, если бы EF обращался только к таблице персон, то как бы ему пришлось реализовывать данный код?
Логично, что ему пришлось бы в момент кастинга (или при первом обращении EmployeeNumber) заново лезть в базу всего лишь за 1 полем!
А теперь представьте, сколько таких запросов он сгенерирует всего за один проход? Выгоднее, вытянуть все сразу.

Кстати, расскажите как работает NH, я помню, что он может использовать proxy для связанных сущностей, но не помню, чтобы proxy использовались при прямой загрузки (впрочем, возможно, можно выставить и такие опции для Lazy-загрузки). Только вот даже если это возможно, не уверен, что вы что-то выиграете от оптимизации всего 1-го запроса. Проиграете на последующих.

А если вас смущают накладные на подцепление таблицы, то вообще-то Table-per-Type не самый оптимальный вариант мапинга, для многих задач Table-per-Hierarchy - гораздо удобнее. Именно потому, что все схожие типы в одной таблице.

NHibernate дает ровно столько, сколько от него просишь дать. При этом SQL получается лучше.

У меня сложилось обратное впечатление - нужно очень четко понимать что будет делать NH, чтобы не влипнуть в деградацию производительности. Причем Lazy-загрузка - это лидер по количеству ошибок неикушенных разработчиков.

Я нашел способ получения правильного запроса, т.е. только по таблице Persons

Поделитесь, если не сложно, интересно.
17 фев 10, 13:18    [8357342]     Ответить | Цитировать Сообщить модератору
 Re: Негерация запросов в EF 4.0  [new]
Dmitry Sukhovilin
Member

Откуда: Таганрог
Сообщений: 363
МихаилР
автор
А это не имеет значения, т.к. менеджер это тоже персона.


Именно!
Хорошо, вот смотрите простейший пример (модификация вашего):
Domain.TestDBEntities c = new Domain.TestDBEntities();
foreach (var p in c.Persons)
{
   Console.Write(p.FirstName + " " + p.LastName + " ");
   Domain.Manager m = p as Domain.Manager;
   if (m != null)
   {
       Console.Write("это менеджер с " + m.EmployeeNumber + " подчиненных");
   }
   Console.WriteLine();
}

Как вы думете, если бы EF обращался только к таблице персон, то как бы ему пришлось реализовывать данный код?


Если я указываю, что мне нужно получить персон - это значит, что мне нужны персоны. Такой код работать не должен, конечно хорошо, со стороны C#, что так можно, но это не то, что мне нужно.
Вообщем должны быть варианты.

МихаилР

Логично, что ему пришлось бы в момент кастинга (или при первом обращении EmployeeNumber) заново лезть в базу всего лишь за 1 полем!
А теперь представьте, сколько таких запросов он сгенерирует всего за один проход? Выгоднее, вытянуть все сразу.


Описанный сценарий записи от конкретной решаемой задачи. Впролне вероятно, что в Persons у меня попадут 1-2 записи. В этом случае не будет большой проблемой сходить на sql за 1 полем.
А вообще, нужно иметь возможность более тонкого тюнинга.

МихаилР

Кстати, расскажите как работает NH, я помню, что он может использовать proxy для связанных сущностей, но не помню, чтобы proxy использовались при прямой загрузки (впрочем, возможно, можно выставить и такие опции для Lazy-загрузки). Только вот даже если это возможно, не уверен, что вы что-то выиграете от оптимизации всего 1-го запроса. Проиграете на последующих.


Вынужден констатировать факт, что NHibernate, зараза, поступает почти так же. Но, SQL более вменяемый генерит.

SELECT     person0_.PersonId AS PersonId0_, person0_.Version AS Version0_, person0_.FirstName AS FirstName0_, 
	person0_.LastName AS LastName0_, 
                      person0_1_.EmployeeNumber AS Employee2_1_, 
                      CASE WHEN person0_1_.PersonId IS NOT NULL 
                      THEN 1 WHEN person0_.PersonId IS NOT NULL 
                      THEN 0 END AS clazz_
FROM         Persons AS person0_ LEFT OUTER JOIN
                      Managers AS person0_1_ ON person0_.PersonId = person0_1_.PersonId

NHibernate, в этом пока ведет.


МихаилР

А если вас смущают накладные на подцепление таблицы, то вообще-то Table-per-Type не самый оптимальный вариант мапинга, для многих задач Table-per-Hierarchy - гораздо удобнее. Именно потому, что все схожие типы в одной таблице.


Меня смущает не возможность контролировать обязательность полей, в схеме TPH.
А в производительности, да, быстрее.

МихаилР

NHibernate дает ровно столько, сколько от него просишь дать. При этом SQL получается лучше.

У меня сложилось обратное впечатление - нужно очень четко понимать что будет делать NH, чтобы не влипнуть в деградацию производительности. Причем Lazy-загрузка - это лидер по количеству ошибок неикушенных разработчиков.

Я нашел способ получения правильного запроса, т.е. только по таблице Persons

Поделитесь, если не сложно, интересно.


Все очень просто:
var qeury = from p in dc.Persons
         select new 
        {p.PersonId, p.FirstName, p.LastName};
17 фев 10, 14:23    [8358035]     Ответить | Цитировать Сообщить модератору
 Re: Негерация запросов в EF 4.0  [new]
МихаилР
Member

Откуда: Ижевск
Сообщений: 255
Dmitry Sukhovilin,

Вынужден констатировать факт, что NHibernate, зараза, поступает почти так же. Но, SQL более вменяемый генерит


Собственно, если вы немного поразмышляете над моим примером, то придете к выводу, что другой вариант, это только Lazy-инициализация, а она почти всегда слишком накладна.
А вменяемость кода... Ничего не могу сказать - вроде план запроса для первого вашего варианта и так оптимальный для такого варианта, хоть использовать подзапрос, хоть нет.

Меня смущает не возможность контролировать обязательность полей, в схеме TPH.
А в производительности, да, быстрее.


Не очень понял, если честно.
Вы имеете в виду, что нельзя указать поле как необязательное в предке, а затем сделать его обязательным в потомке? Если да, то и в TPT для EF это вроде не возможно... Или вы о чем-то другом?

Все очень просто

Ну, да, логично :).
17 фев 10, 14:45    [8358252]     Ответить | Цитировать Сообщить модератору
 Re: Негерация запросов в EF 4.0  [new]
Dmitry Sukhovilin
Member

Откуда: Таганрог
Сообщений: 363
МихаилР
Dmitry Sukhovilin,
Вы имеете в виду, что нельзя указать поле как необязательное в предке, а затем сделать его обязательным в потомке? Если да, то и в TPT для EF это вроде не возможно... Или вы о чем-то другом?


Нельзя гарантировать на уровне БД обязательность поля. В TPT я могу сказать, что EmployeeCount должно не сожержать NULL, в TPH так нельзя, это большой "-".
17 фев 10, 15:23    [8358615]     Ответить | Цитировать Сообщить модератору
Все форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM Ответить