Почему EF 4.1 не поддерживает сложные запросы так же, как linq-to-sql? - PullRequest
9 голосов
/ 20 июня 2011

Я нахожусь в процессе преобразования нашего внутреннего веб-приложения из Linq-To-Sql в EF CodeFirst из существующей базы данных.В последнее время меня все больше и больше раздражают ограничения Linq-To-Sql, и необходимость обновлять edmx после обновления очень переплетенной таблицы базы данных окончательно разочаровала меня, чтобы перейти на EF.

Однако я сталкиваюсь сВ некоторых ситуациях использование linq с Linq-To-Sql более эффективно, чем в последней платформе Entity Framework, и мне интересно, кто-нибудь знает причину этого?Большая часть этого, кажется, имеет дело с преобразованиями.Например, следующий запрос работает в L2S, но не в EF:

        var client = (from c in _context.Clients
                      where c.id == id
                      select ClientViewModel.ConvertFromEntity(c)).First();

В L2S это корректно извлекает клиента из базы данных и преобразует его в тип ClientViewModel, но в EF это исключение говорит:что Linq to Entities не распознает метод (что имеет смысл, как я его написал.

Чтобы заставить это работать в EF, я должен переместить select на после First() вызова.

Другим примером является мой запрос на получение списка клиентов. В моем запросе я преобразую его в анонимную структуру для преобразования в JSON:

        var clients = (from c in _context.Clients
                       orderby c.name ascending
                       select new
                       {
                           id = c.id,
                           name = c.name,
                           versionString = Utils.GetVersionString(c.ProdVersion),
                           versionName = c.ProdVersion.name,
                           date = c.prod_deploy_date.ToString()
                       })
                       .ToList();

Мало того, что Utils.GetVersionString()Метод вызывает исключение неподдерживаемого метода в EF, c.prod_deploy_date.ToString() также вызывает его, и это простой DateTime. Как и ранее, для его исправления мне пришлось выполнить преобразование выбора после ToList().


Редактировать : Еще один случай, с которым я только что столкнулся, заключается в том, что EF не может обрабатывать операторы where, которые сравнивают сущности, в которых L2S не имеет проблем с ним.бросатьЭто исключение, и вместо этого я должен сделать
context.TfsWorkItemTags.Where(x => x.TfsWorkItem.id == tfsWorkItemEntity.id).ToList() 


Редактировать 2 : Я хотел добавить еще одну проблему, которую нашел.Очевидно, вы не можете использовать массивы в запросах EF Linq, и это, вероятно, раздражает меня больше всего.Так, например, прямо сейчас я конвертирую сущность, обозначающую версию, в int[4] и пытаюсь запросить ее.В Linq-to-Sql я использовал следующий запрос:
return context.ReleaseVersions.Where(x => x.major_version == ver[0] && x.minor_version == ver[1]
                                          && x.build_version == ver[2] && x.revision_version == ver[3])
                              .Count() > 0;

Сбой со следующим исключением:

The LINQ expression node type 'ArrayIndex' is not supported in LINQ to Entities.

Редактировать 3 : Iнашел другой пример плохой реализации Linq в EF.Ниже приведен запрос, который работает в L2S, но не работает в EF 4.1:

        DateTime curDate = DateTime.Now.Date;
        var reqs = _context.TestRequests.Where(x => DateTime.Now > (curDate + x.scheduled_time.Value)).ToList();

Это выдает ArgumentException с сообщением DbArithmeticExpression arguments must have a numeric common type.


Почему этокажется, они понизили способность запросов Linq в EF, чем в L2S?

Ответы [ 3 ]

15 голосов
/ 20 июня 2011

Редактировать (02.09.2012): Обновлено с учетом .NET 4.5 и добавлено несколько недостающих функций

Это не ответ - это не может быть, потому что только квалифицированный специалисткто может ответить на ваш вопрос, вероятно, менеджер по продукту из команды ADO.NET.

Если вы проверите набор функций старых наборов данных, затем linq-to-sql, а затем EF, вы обнаружите, что критические функции удалены в более новых APIпотому что новые API разрабатываются в гораздо более короткие сроки с большими усилиями по предоставлению новых модных функций.

Просто список некоторых важных функций, доступных в DataSets, но недоступных в последующих API:

  • Пакетная обработка
  • Уникальные ключи

Функции, доступные в Linq-to-Sql, но не поддерживаемые в EF (возможно, список не полностью правильный, я долгое время не использовал L2S):

  • Ведение журнала активности базы данных
  • Свойства с отложенной загрузкой
  • Левое внешнее соединение (DefaultIfEmpty) с первой версии (EF имеет его с EFv4)
  • Глобальные определения загрузки с нетерпением
  • AssociateWith -например, условия для загруженных данных
  • Сначала код с первой версии
  • IMultipleResults, поддерживающий хранимые процедуры, возвращающие несколько наборов результатов (EF имеет это в .NET 4.5, но дизайнер не поддерживаетэта функция)
  • Поддержка табличных функций (EF имеет это в .NET 4.5)
  • и некоторых других

Теперь мы можем перечислить функции, доступные в EF ObjectContextAPI (EFv4) и отсутствует в DbCoAPI ntext (EFv4.1):

  • Отображение хранимых процедур
  • Условное отображение
  • Отображение функций базы данных
  • Определение запросов, QueryViews, Определенная модельфункции
  • ESQL недоступен, если вы не конвертируете DbContext обратно в ObjectContext
  • Управление состоянием независимых отношений невозможно, если вы не конвертируете DbContext обратно в ObjectContext
  • Использование MergeOption.OverwriteChanges и MergeOption.PreserveChanges невозможно, если вы не конвертируете DbContext обратно в ObjectContext
  • и некоторых других

Мои личные чувства по этому поводу толькобольшая грустьОсновные функции отсутствуют, а функции, существующие в предыдущих API, удалены, потому что команде ADO.NET явно не хватает ресурсов для их реализации - во многих случаях путь миграции практически невозможен.В целом ситуация еще хуже, потому что отсутствующие функции или препятствия миграции не перечислены напрямую (боюсь, даже команда ADO.NET не узнает о них, пока кто-то не сообщит о них).

Из-за этого я думаю, что вся идея DbContext API была ошибкой управления.На данный момент команда ADO.NET должна поддерживать два API - DbContext не подходит для замены ObjectContext и на самом деле не может, потому что это просто оболочка и из-за этого ObjectContext не может умереть.Скорее всего, ресурсы, доступные для разработки EF, сократились вдвое.

С этим связано больше проблем.Как только мы покинем команду ADO.NET и посмотрим на проблему с точки зрения набора продуктов MS, мы увидим так много несоответствий, что я иногда даже задаюсь вопросом, существует ли какая-либо глобальная стратегия.

Просто согласитесь с тем, что поставщик EF работает по-другому, и запросы, которые работали в Linq-to-sql, не должны работать с EF.

7 голосов
/ 24 февраля 2012

Немного опоздал к игре, но я нашел этот пост, когда искал что-то еще, и подумал, что я отправлю ответ на фундаментальные вопросы в оригинальном посте, который в основном сводится к «LINQ to SQL позволяет Expression[x], но EF нет ".

Ответ заключается в том, что поставщик запросов (код, который переводит ваше дерево выражений LINQ в нечто, что фактически выполняется и возвращает перечисляемый набор вещей), принципиально отличается междуL2S и EF.Чтобы понять почему, вы должны понимать, что еще одно фундаментальное отличие между L2S и EF состоит в том, что L2S основан на таблицах, а EF основан на объектных моделях.Другими словами, EF работает с концептуальными моделями сущностей и знает, что базовая физическая модель (таблицы БД) не обязательно отражает концептуальные сущности.Это потому, что таблицы нормализованы / денормализованы, и имеют странные способы работы с обобщением типа сущности (наследование).Таким образом, в EF у вас есть представление о концептуальной модели (объектах, с которыми вы кодируете в VB / C # и т. Д.) И сопоставлении с физическими базовыми таблицами, которые составляют ваши концептуальные сущности.L2S не делает этого.Каждая «модельная сущность» в L2S является строго одной таблицей, с точно такими же полями таблицы, как есть.

Пока что это само по себе не объясняет проблемы в первоначальном посте, но, надеюсь,, вы можете начать понимать, что принципиально, EF - это не L2S + или L2S v4.0.Это совершенно другой вид продукта (настоящий ORM), даже если есть некоторое совпадение в том факте, что оба используют LINQ для получения данных базы данных.

Еще одно интересное отличие заключается в том, что EF был построен с нулябыть независимым от БД, тогда как L2S работает только против MS SQL Server (хотя любой, кто достаточно глубоко изучил код L2S, увидит, что есть некоторые основания, позволяющие использовать разные БД, но в итоге он был привязан к MS SQL Serverтолько).Это различие также играет большую роль в том, почему некоторые выражения работают в L2S LINQ, но не в EF LINQ.Поставщик запросов EF имеет дело с каноническими выражениями БД, что на простом английском языке означает выражения LINQ, которые имеют эквиваленты SQL-запросов практически во всех реляционных базах данных.Базовый EF-механизм (поставщик запросов) преобразует выражения LINQ в эти канонические выражения DB, а затем передает дерево канонических выражений конкретному поставщику DB (скажем, поставщику EF Oracle или MySQL), где оно преобразуется в специфичный для продукта SQL.Здесь вы можете увидеть, как эти канонические выражения должны переводиться отдельными поставщиками продуктов: http://msdn.microsoft.com/en-us/library/ee789836.aspx

Кроме того, EF позволяет использовать некоторые функции БД (функции хранилища) для конкретных продуктов в виде выражений через расширения.Базовые поставщики продуктов и услуг отвечают как за предоставление, так и за их перевод.

В этом случае EF допускает только выражения, являющиеся каноническими выражениями БД, или функции, специфичные для магазина, потому что all выражения в дереве преобразуются в SQL для выполнения в БД.

Разница с L2S заключается в том, что L2S передает все возможные выражения в БД из своего ограниченного генератора SQL, а затем выполняет любые выражения, которые могутне переводить в SQL для материализованного набора объектов, который возвращается.Хотя это упрощает использование L2S, но вы не видите, что половина ваших выражений фактически не попадает в БД в виде SQL, и это может привести к некоторым действительно неэффективным запросам, возвращающим большие наборы данных, которые затем повторяются.снова в памяти CLR с обычным объектом LINQ, действующим против них для других выражений, которые L2S не может превратить в SQL.

Вы получаете те же самые эффекты в EF, используя EF для возврата материализованных данных в наборы объектов в памяти, а затем используя дополнительные операторы LINQ для этого набора в памяти - как это делает L2S, но в этом случае вы просто сделать это явно, точно так же, как когда вы говорите, что должны вызывать .First () перед использованием не-DB-канонического выражения. Точно так же вы можете вызвать .ToArray () или .ToList () перед использованием дополнительных выражений, которые нельзя превратить в SQL.

Еще одно большое отличие состоит в том, что в EF сущности должны использоваться как единое целое. Реальные модельные объекты представляют собой концептуальные объекты, которые обрабатываются в целом. Например, у вас никогда не бывает половины пользователей. Пользователь - это объект, состояние которого зависит от всех полей. Если вы хотите вернуть частичные объекты или плоское объединение нескольких объектов, вам нужно определить проекцию (то, что EF называет сложным типом), или вы можете использовать некоторые из новых функций POCO 4.1 / 4.2 / 4.3.

1 голос
/ 21 сентября 2013

Теперь, когда Entity Framework является открытым исходным кодом , достаточно легко увидеть, особенно из комментариев к вопросам от команды, что одна из целей состоит в том, чтобы предоставить EF в качестве открытого слоя поверх множествабазы данных.Честно говоря, Microsoft, что неудивительно, реализовала этот слой только поверх своего SQL Server, но есть и другие реализации, такие как MySql EF Connector DevArt .

. В рамках этой цели целесообразночтобы ограничить общедоступный интерфейс, и пытаться добавить дополнительный слой, который спрашивает - ну, кое-что из этого может быть сделано в памяти, кое-что из этого может быть сделано в SQL, кто знает - определенно усложняет работу для других разработчиков, пытающихсяпривязать EF к той или иной базе данных.

Итак, я согласен с другим ответом здесь - вам нужно будет спросить команду - но вы также можете получить много информации о направлении этой команды на общедоступную ошибкуtracker и другие его публикации, и это кажется очевидной мотивацией.

Тем не менее основное различие между LINQ to SQL и EF заключается в том, что EF создает исключение для кода, который должен быть запущен в памяти, и еслиВы ниндзя Expressions, ничто не мешает вам сделать следующий шаг, чтобы обернуть DbКонтекстный класс и заставить его работать так же, как LINQ to SQL.С другой стороны, вы получаете смешанный пакет - вы делаете его неявным, а не явным, когда генерируется SQL и когда он запускается, и это можно рассматривать как потерю производительности и контроля в обмен на гибкость / простотуавторского права.

...