Entity Framework (4.2) HasRequired приводит к неожиданному левому внешнему соединению - PullRequest
9 голосов
/ 16 декабря 2011

Похоже, что Entity Framework (последняя версия от NuGet) может игнорировать конфигурацию HasRequired при создании соединений для свойств навигации, отличных от первого определенного.

Например, учитывая объект POCO (Person)со следующей конфигурацией:

var person = modelBuilder.Entity<Person>();
person.ToTable("The_Peoples");
person.HasKey(i => i.Id);
person.Property(i => i.Id).HasColumnName("the_people_id");
person.HasRequired(i => i.Address)
    .WithMany()
    .Map(map => map.MapKey("address_id"));
person.HasRequired(i => i.WorkPlace)
    .WithMany()
    .Map(map => map.MapKey("work_place_id"));

Я пытаюсь загрузить список людей со следующим запросом:

myContext.Set<People>()
    .Include(o => o.Address)
    .Include(o => o.WorkPlace);

Entity Framework создает следующий запрос:

FROM  [dbo].[The_Peoples] AS [Extent1]
INNER JOIN [dbo].[The_Addresses] AS [Extent2] ON [Extent1].[address_id] = [Extent2].[address_id]
LEFT OUTER JOIN [dbo].[The_Work_Places] AS [Extent3] ON [Extent1].[work_place_id] = [Extent3].[work_place_id]

Обратите внимание, что соединение с таблицей * The_Addresses * является внутренним соединением (как и ожидалось), однако последующее соединение с * The_Work_Places * является внешним соединением.Учитывая, что свойства Address и WorkPlace помечены как обязательные, я ожидаю, что оба объединения будут внутренними.Я также пытался пометить свойства Address и WorkPlace атрибутом Required, но это никак не отразилось.

Это ошибка или, возможно, я что-то неправильно настроил?Предложения?

Ответы [ 3 ]

7 голосов
/ 17 декабря 2011

Конфигурация вашей модели правильная, и я думаю, что это не ошибка, но это поведение по дизайну, но я не могу точно сказать, какой дизайн.Я также видел этот SQL в таких запросах.Только несколько замечаний:

  • Запрос, который вы видите, не относится к EF 4.2.Это также произойдет для EF 4.1 и EF 4.0.Но не для EF 1 (.NET 3.5).В EF 1 каждое Include, также первое, сопоставлено с LEFT OUTER JOIN, также для требуемых отношений.

  • Я думаю, что нельзя сказать, что использование INNER JOIN"правильный" для требуемых навигационных свойств и LEFT OUTER JOIN неверен.С точки зрения отображения не имеет значения, что вы используете, учитывая, что ограничения в базе данных правильно представляют отношения в модели.Для обязательного свойства навигации столбец FK в базе данных не должен иметь значения NULL, и в базе данных должно быть ограничение, обеспечивающее, что FK ссылается на существующую строку в целевой таблице.Если это так, каждый JOIN должен возвращать строку, независимо от того, используете ли вы INNER JOIN или LEFT OUTER JOIN.

  • Что произойдет, если модель и база данных "не в сети"синхронизировать "относительно отношений?По сути, бессмыслица происходит в обоих случаях: если вы используете LEFT OUTER JOIN и FK равен NULL в БД или ссылается на несуществующую строку, вы получите объект, у которого свойство навигации равно null, что нарушает модельопределение того, что свойство обязательно.Использование INNER JOIN не лучше: вы вообще не получите никакой сущности, результат запроса, который по крайней мере такой же неправильный, как результат с LEFT OUTER JOIN, если не хуже.

  • Итак, я думаю, что изменение в .NET 4 для использования INNER JOIN s для некоторых Include s было сделано не потому, что SQL в EF 1 был неправильным, а для создания лучшего и более производительного SQL.Это изменение фактически внесло существенное изменение в то, что некоторые запросы теперь возвращают другие результаты, чем в EF 1: http://thedatafarm.com/blog/data-access/ef4-breaking-change-ef4-inner-joins-affect-eager-loading-many-to-many/

  • Насколько я понимаю, это было исправлено и причина была в том, что INNER JOIN в слишком многих ситуациях было введено для активной загрузки в EF 4. (Возможно, на этом этапе (кандидат на бета-версию / релиз для EF 4) ваш запрос имел бы два INNER JOIN s.) Ответ на эту проблему из EFкоманда: http://connect.microsoft.com/VisualStudio/feedback/details/534675/ef4-include-method-returns-different-results-than-ef1-include (выделение жирным шрифтом от меня):

    Мы исправляем проблему для .net 4 RTM.Это было непреднамеренное критическое изменение. Мы не делали предполагаемого изменения, чтобы каждое левое внешнее объединение, созданное с помощью Include, стало внутренним объединением в .Net 4. Скорее, оптимизация рассмотрела ограничения в метаданных EF и попыталась преобразовать эти оставленные внешниеобъединения, которые могут быть безопасно преобразованы во внутренние объединения на основе ограничений.У нас была ошибка в коде, в которой мы рассуждали на основе ограничений, что приводило к более агрессивному преобразованию, чем то, что подразумевали ограничения. Мы сократили оптимизацию, чтобы преобразовать левые внешние объединения во внутренние объединения только в тех местах, где мы абсолютно уверены, что можем сделать это на основе ограничений. Мы думаем, что сможем немного улучшить эту оптимизациюв будущем. Вы увидите больше левых внешних объединений для некоторых запросов в RTM по сравнению с RC и Beta 2, но в большинстве этих случаев это необходимо для получения правильных результатов.

    ИтакВ окончательном выпуске EF 4, по-видимому, вновь появилось еще LEFT OUTER JOIN с (по сравнению с бета / кандидатом на выпуск), чтобы избежать таких критических изменений.чем реальное объяснение, почему вы получаете INNER JOIN, а затем LEFT OUTER JOIN.Как уже говорилось, не так неправильно писать запрос так, как было бы неправильно использовать два INNER JOIN с или два LEFT OUTER JOIN с.Я предполагаю, что только команда EF может объяснить, почему ваш запрос производит именно этот SQL.

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

0 голосов
/ 21 марта 2019

Попробуйте использовать WithRequiredDependent() вместо WithMany()

0 голосов
/ 23 октября 2013

Это похоже на некро, но сегодня у меня возникла такая же проблема ...

проблема с этим:

FROM  [dbo].[The_Peoples] AS [Extent1]
INNER JOIN [dbo].[The_Addresses] AS [Extent2] ON [Extent1].[address_id] = [Extent2].[address_id]
LEFT OUTER JOIN [dbo].[The_Work_Places] AS [Extent3] ON [Extent1].[work_place_id] = [Extent3].[work_place_id]

- это когда вы применяете фильтр из [dbo].[The_Peoples], вы указываете применить фильтры к объединению, поэтому это занимает меньше времени, но мы обнаружили (вы можете запустить план запроса, чтобы увидеть эту проблему), что оно выполняет объединение со всем содержимым таблиц, затемпозже он применяет фильтр ... который отнимает много дополнительного времени ... в нашем случае он запускает тайм-аут, запрос, который должен занимать от 1 до 3 секунд, занимал 1+ минуту

...