Недавно я столкнулся с вопросом на форуме Entity Framework на msdn: http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/bb72fae4-0709-48f2-8f85-31d0b6a85f68
Человек, задавший вопрос, попытался выполнить относительно простой запрос, включающий две таблицы, группировку, сортировку по и агрегирование с использованием Linq-to-Entities. Довольно простой запрос Linq, который также легко выполнить в SQL - то, что люди пытаются делать каждый день.
Однако при использовании Linq-to-Entities результатом является сложный запрос с множеством ненужных объединений и т. Д. Я попробовал и не смог заставить Linq-to-Entities сгенерировать из него достойный SQL-запрос, если использовал только чистый Linq против сущностей EF.
Увидев изрядную долю запросов-монстров из EF, я подумал, что, возможно, OP (и я, и другие) что-то делают неправильно. Может есть способ лучше сделать?
Итак, вот моя задача: использовать пример из форума EF и использование только Linq-to-Entities для двух сущностей, возможно ли заставить EF сгенерировать запрос SQL без ненужных объединений и других сложностей?
Я хотел бы, чтобы EF генерировал что-то немного более близкое к тому, что Linq-to-SQL делает для того же типа запросов, при этом используя Linq против модели EF.
Ограничения: используйте EFv1 .net 3.5 SP1 или EFv4 (бета-версия 1 является частью бета-версии VS2010 / .net4, доступной для загрузки от Microsoft). Не разрешены трюки сопоставления CSDL-> SSDL, "определяющие запросы" модели, хранимые процессы, функции на стороне базы данных или представления. Просто простое сопоставление 1: 1 между моделью и базой данных и чистый запрос L2E, который выполняет то, что запрашивал исходный поток в MSDN. Между двумя объектами должна существовать связь (то есть мой "обходной путь №1" ответ исходному потоку не является допустимым обходным путем)
Обновление: добавлено вознаграждение в размере 500 пунктов. Развлекайся.
Обновление: как упоминалось выше, решение, использующее EFv4 / .net 4 (β1 или новее), конечно, имеет право на вознаграждение. Если вы используете .net 4 post β1, укажите номер сборки (например, 4.0.20605), используемый вами запрос L2E и SQL, который он сгенерировал и отправил в БД.
Обновление: эта проблема была исправлена в VS2010 / .net 4 beta 2. Хотя сгенерированный SQL по-прежнему имеет несколько [относительно безобидных] дополнительных уровней вложенности, он не делает ничего лишнего. вещи, к которым он привык. Окончательный план выполнения после того, как оптимизатор SQL Server опробовал его, теперь настолько хорош, насколько это возможно. +++ для парней и парней, ответственных за часть EFv4, генерирующую SQL ...
Ответ 1 · рейтинг 2 · Lusid
Если бы меня так беспокоил сумасшедший SQL, я бы просто не выполнял группировку в базе данных. Сначала я бы запросил все данные, которые мне нужны, завершив их с помощью ToList (), используя функцию Include для загрузки всех данных за один выбор.
Вот мой окончательный результат:
var list = from o in _entities.orderT.Include("personT")
.Where(p => p.personT.person_id == person_id &&
p.personT.created >= fromTime &&
p.personT.created <= toTime).ToList()
group o by new { o.name, o.personT.created.Year, o.personT.created.Month, o.personT.created.Day } into g
orderby g.Key.name
select new { g.Key, count = g.Sum(x => x.price) };
Это приводит к гораздо более простому выбору:
SELECT
1 AS [C1],
[Extent1].[order_id] AS [order_id],
[Extent1].[name] AS [name],
[Extent1].[created] AS [created],
[Extent1].[price] AS [price],
[Extent4].[person_id] AS [person_id],
[Extent4].[first_name] AS [first_name],
[Extent4].[last_name] AS [last_name],
[Extent4].[created] AS [created1]
FROM [dbo].[orderT] AS [Extent1]
LEFT OUTER JOIN [dbo].[personT] AS [Extent2] ON [Extent1].[person_id] = [Extent2].[person_id]
INNER JOIN [dbo].[personT] AS [Extent3] ON [Extent1].[person_id] = [Extent3].[person_id]
LEFT OUTER JOIN [dbo].[personT] AS [Extent4] ON [Extent1].[person_id] = [Extent4].[person_id]
WHERE ([Extent1].[person_id] = @p__linq__1) AND ([Extent2].[created] >= @p__linq__2) AND ([Extent3].[created] <= @p__linq__3)
Кроме того, с предоставленными данными в качестве примера SQL Profiler замечает увеличение продолжительности вызова SQL только на 3 мс.
Лично я думаю, что любой, кто жалуется на то, что ему не нравится выходной SQL уровня ORM, должен вернуться к использованию хранимых процедур и наборов данных. Они просто еще не готовы к развитию, и им нужно провести еще несколько лет в пресловутой печи. :)
Ответ 2 · рейтинг 2 · Community
Интересное обсуждение. До сих пор я использовал 2 модели ORM (NHibernate и LINQ-to-Entities). По моему опыту, всегда есть линия, в которой вам нужно отказаться от ORM для создания SQL и вернуться к хранимым процедурам или представлениям для достижения наилучших масштабируемых запросов. Сказав это, я лично считаю, что LINQ лучше работает с более нормализованными базами данных, и все вложенные запросы / соединения не являются серьезной проблемой. В некоторых случаях для повышения производительности или масштабируемости вам необходимо использовать функции сервера БД (индексированные представления, например, в SQL 2008 SE работают только с подсказками запросов), и вы просто не можете использовать ORM (кроме iBatis?).
Конечно, вы не получите наилучшей производительности или масштабируемости, используя эти вложенные соединения / запросы, сгенерированные linq, но, пожалуйста, не забывайте о преимуществах и преимуществах разработки, предоставляемых LINQ (или NHibernate) в любом проекте. Несомненно, в этом должна быть какая-то заслуга.
Наконец, хотя я и рискую сравнивать яблоко и апельсины, но не думаю, что это больше похоже на вопрос: хотите ли вы быстрой разработки веб-сайта (веб-формы asp.net, Swing) или большего контроля над вашим HTML (asp.net mvc, RoR)? подберите то, что лучше всего соответствует вашим требованиям.
Мои 2 цента!
Ответ 3 · Ritesh M Nayak
SQL, который генерирует linq, очень эффективен. Это может показаться громоздким, но в нем учтены отношения в таблицах, ограничения и т. Д. На мой взгляд, вам следует просто слепо использовать команды linq и не беспокоиться о масштабе. У больших запросов есть преимущества, поскольку они генерируются автоматически. Он избегает любых ошибок в реляционных ограничениях и добавляет свои собственные оболочки для ошибок / исключений.
Однако, если вы хотите написать SQL самостоятельно и по-прежнему хотите работать в рамках ORM, попробуйте iBatis http://ibatis.apache.org/ Вы должны написать SQL и присоединиться сами, так что это дает вам полный контроль над бэкэнд-моделью.
Лично просто используйте SQLMetal и linq. Не беспокойтесь о производительности и масштабе, если вам это не нужно.