Задача: заставить Linq-to-Entities генерировать достойный SQL без ненужных объединений - PullRequest
5 голосов
/ 29 мая 2009

Недавно я столкнулся с вопросом на форуме 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» на исходный поток не является допустимым обходным решением)

Обновление: Добавлена ​​награда в 500pt. Веселитесь.

Обновление: Как уже упоминалось выше, решение, которое использует EFv4 / .net 4 (β1 или более позднюю версию), безусловно, имеет право на получение награды. Если вы используете .net 4 post β1, пожалуйста, укажите номер сборки (например, 4.0.20605), использованный вами запрос L2E и SQL, который он сгенерировал и отправил в БД.

Обновление: Эта проблема была исправлена ​​в VS2010 / .net 4 beta 2. Хотя сгенерированный SQL все еще имеет несколько [относительно безопасных] дополнительных уровней вложенности, он не выполняет никаких сумасшедшие вещи, к которым он привык. Окончательный план выполнения после того, как оптимизатор SQL Server попытался это сделать, теперь настолько хорош, насколько это возможно. +++ для парней и парней, ответственных за генерирующую SQL часть EFv4 ...

Ответы [ 3 ]

2 голосов
/ 19 августа 2009

Интересная дискуссия. До сих пор я использовал 2 модели ORM (NHibernate и LINQ-to-Entities). По моему опыту, всегда есть линия, где вы должны отказаться от ORM для генерируемого SQL и прибегнуть к хранимым процедурам или представлениям для достижения наилучших масштабируемых запросов. Сказав это, я лично считаю, что LINQ лучше работает с более нормализованными базами данных, и все вложенные запросы / объединения не являются серьезной проблемой. В некоторых случаях для повышения производительности или масштабируемости необходимо использовать функции сервера БД (индексированные представления, например, в SQL 2008 SE работают только с подсказками запросов), и вы просто не можете использовать ORM (кроме iBatis?).

Конечно, вы не получите наилучшую производительность или масштабируемость, используя эти вложенные объединения / запросы, сгенерированные linq, но не забывайте о преимуществах и преимуществах разработки, предоставляемых LINQ (или NHibernate) в любом проекте. Конечно, в этом должна быть какая-то заслуга.

Наконец, хотя я рискую сравнивать яблоки и апельсины, но больше не думаю, что спрашиваю: хотите ли вы быстрой разработки сайтов (asp.net webforms, swing) или больше контроля над своим HTML (asp.net mvc, RoR)? выберите вещь, которая наилучшим образом соответствует вашим требованиям.

Мои 2 цента!

2 голосов
/ 02 июня 2009

Если бы меня волновал сумасшедший 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, должен вернуться к использованию хранимых процедур и наборов данных. Они просто еще не готовы развиваться, и им нужно провести еще несколько лет в печально известной печи. :)

0 голосов
/ 02 июня 2009

SQL, который генерирует linq, очень эффективен. Это может выглядеть громоздко, но учитывает отношения к таблицам, ограничениям и т. Д. На мой взгляд, вы должны просто слепо использовать команды linq и не беспокоиться о масштабе. Преимущества больших запросов заключаются в том, что они генерируются автоматически. Это позволяет избежать любых ошибок в реляционных ограничениях и добавляет свои собственные оболочки для ошибок / исключений.

Если, однако, вы хотите написать SQL самостоятельно и по-прежнему работать за пределами ORM, попробуйте iBatis http://ibatis.apache.org/ Вы должны написать SQL и присоединиться самостоятельно, чтобы он полностью контролировал Бэкэнд-модель.

Лично просто используйте SQLMetal и linq. Не беспокойтесь о производительности и масштабе, если вам не нужно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...