powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / Негерация запросов в EF 4.0
10 сообщений из 10, страница 1 из 1
Негерация запросов в EF 4.0
    #36471984
Dmitry Sukhovilin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Добрый день,

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

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

Код: plaintext
1.
2.
3.
4.
5.
Domain.TestDBEntities c = new Domain.TestDBEntities();
foreach (var p in c.Persons)
{
	Console.WriteLine(p.FirstName + " " + p.LastName);
}

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

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
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%. Т.е. это БОЛЬШЕ ПОЛОВИНЫ !
Кроме того,

Код: plaintext
1.
2.
3.
4.
5.
6.
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.

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

Спасибо.
...
Рейтинг: 0 / 0
Негерация запросов в EF 4.0
    #36471988
Dmitry Sukhovilin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
А вот и план
...
Рейтинг: 0 / 0
Негерация запросов в EF 4.0
    #36472027
Dmitry Sukhovilin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Even more,
Код: plaintext
1.
var query = from p in c.Persons
select p;

Всеравно делает обращение в Managers
...
Рейтинг: 0 / 0
Негерация запросов в EF 4.0
    #36474187
МихаилР
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну, то, что при простом обращении
Код: plaintext
c.Persons
он подтягивает Managers - вполне понятно и логично, чтобы инстанцировать конкретный объект ему нужна информация из обеих таблиц - как иначе узнать заранее будет ли конкретный объект просто персоной или уже менеджером ?
Вариантов 2:
- или вытаскивать все, что в принципе может пригодится, 1 запросом.
- либо достать ведущую таблицу, определить тип каждого элемента и для тех, кто не "не просто персона" - достать еще дополнительные поля (этакая Lazy-инициализация).
В большинстве случаев первый вариант предпочтительнее. По крайней мере, на сколько я помню, в NH реализация аналогичная.

Почему не срабатывает
Код: plaintext
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# будет описано аналогичное поведение - в выборку попадает как базовый, так и наследуемые типы.
...
Рейтинг: 0 / 0
Негерация запросов в EF 4.0
    #36474321
Dmitry Sukhovilin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
МихаилРНу, то, что при простом обращении
Код: plaintext
c.Persons
он подтягивает Managers - вполне понятно и логично, чтобы инстанцировать конкретный объект ему нужна информация из обеих таблиц - как иначе узнать заранее будет ли конкретный объект просто персоной или уже менеджером ?

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

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


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

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

МихаилР
Почему не срабатывает
Код: plaintext
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# будет описано аналогичное поведение - в выборку попадает как базовый, так и наследуемые типы.

Спасибо за подробный ответ.
...
Рейтинг: 0 / 0
Негерация запросов в EF 4.0
    #36474394
Dmitry Sukhovilin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Я нашел способ получения правильного запроса, т.е. только по таблице Persons.
...
Рейтинг: 0 / 0
Негерация запросов в EF 4.0
    #36474835
МихаилР
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
авторА это не имеет значения, т.к. менеджер это тоже персона.

Именно!
Хорошо, вот смотрите простейший пример (модификация вашего):
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
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
Поделитесь, если не сложно, интересно.
...
Рейтинг: 0 / 0
Негерация запросов в EF 4.0
    #36475154
Dmitry Sukhovilin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
МихаилРавторА это не имеет значения, т.к. менеджер это тоже персона.

Именно!
Хорошо, вот смотрите простейший пример (модификация вашего):
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
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 более вменяемый генерит.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
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
Поделитесь, если не сложно, интересно.

Все очень просто:
Код: plaintext
1.
2.
3.
var qeury = from p in dc.Persons
         select new 
        {p.PersonId, p.FirstName, p.LastName};
...
Рейтинг: 0 / 0
Негерация запросов в EF 4.0
    #36475238
МихаилР
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Dmitry Sukhovilin,

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

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

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

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

Все очень просто
Ну, да, логично :).
...
Рейтинг: 0 / 0
Негерация запросов в EF 4.0
    #36475388
Dmitry Sukhovilin
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
МихаилРDmitry Sukhovilin,
Вы имеете в виду, что нельзя указать поле как необязательное в предке, а затем сделать его обязательным в потомке? Если да, то и в TPT для EF это вроде не возможно... Или вы о чем-то другом?


Нельзя гарантировать на уровне БД обязательность поля. В TPT я могу сказать, что EmployeeCount должно не сожержать NULL, в TPH так нельзя, это большой "-".
...
Рейтинг: 0 / 0
10 сообщений из 10, страница 1 из 1
Форумы / ADO.NET, LINQ, Entity Framework, NHibernate, DAL, ORM [игнор отключен] [закрыт для гостей] / Негерация запросов в EF 4.0
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]