Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Задача: заставить Linq-to-Entities генерировать достойный SQL без ненужных объединений

Автор вопроса: KristoferAОтветов: 3

Недавно я столкнулся с вопросом на форуме 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 ...

  • Я не понимаю, что вы пытаетесь показать. Я не решаюсь давать ответ, который можно было бы просто использовать, чтобы кого-то обрушить на технологию первой ревизии. Dan Blair 03.06.2009
  • Я не пытаюсь ничего показывать, я хочу знать, возможно ли использование L2E / EF v1 (.net 3.5) или v2 (v4 / .net 4.0) для генерации SQL-запросов без всех избыточных дополнительных соединений и т. Д. I ' Я уверен, что должен быть способ, но я не могу этого сделать. Так что, чтобы удовлетворить свое любопытство, я назначил награду за ответ на относительно простой вопрос - надеюсь, это должно побудить кого-нибудь найти способ сделать это. Причина, по которой я выбрал конкретный пример, заключается в том, что человек, который первоначально задал вопрос, предоставил простую репликацию, используя. KristoferA 03.06.2009
  • ... продолжение ... Если этот простой запрос можно исправить, то, вероятно, можно исправить и более сложные, используя [каков бы ни был ответ на этот вопрос] -технику / состав запроса. KristoferA 03.06.2009

entity-frameworklinq-to-entities

Ответы

Ответ 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. Не беспокойтесь о производительности и масштабе, если вам это не нужно.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы