EF Core 8 не объединяет объекты

При переходе с EF Core 5 на версию 8 выявилась следующая проблема.

var task = await _context.Tasks
    .Include(t => t.Document)
    .ThenInclude(t => t.RouteStepValues)
    .FirstOrDefaultAsync(t => t.Id == taskId);

var documentId = task.Document.ParentDocumentId.HasValue ? 
    task.Document.ParentDocumentId.Value : task.Document.Id;

var document = await _context.Documents
    .Include(d => d.RouteStepValues)
    .Include(t => t.DocumentType)
    .ThenInclude(t => t.Components)
    .ThenInclude(t => t.ComponentPropertyDependencies)
    .ThenInclude(t => t.ElementProperty)
    .FirstOrDefaultAsync(d => d.Id == documentId);

var currentRouteStep = document.RouteStepValues.FirstOrDefault(t => t.TaskId == taskId);

var currentRouteGroup = currentRouteStep.Document.DocumentType.Components.FirstOrDefault(t => t.Id == componentId);

Для меня Components ничего не содержит при запуске этого кода на EF Core 8. В EF Core 5 туда были подтянуты необходимые значения. Загрузил ли я уже необходимые данные документа, они должны были быть локально при запросе из currentRouteStep?

Когда я убрал .Include(t => t.Document).ThenInclude(t => t.RouteStepValues) из запроса task, всё заработало правильно.

Я сделал дополнительные тесты. Данные в коллекции либо есть, либо отсутствуют, вообще непонятно, от чего это зависит. Иногда подтягиваются нормально, иногда коллекция пустая.

Я тоже пробовал получить прямо из документа, ситуация аналогичная.

var currentRouteGroup = document.DocumentType.Components.FirstOrDefault(t => t.Id == componentId);

EF Core не сломан. Мы не можем угадать, что на самом деле делает ваш код или что представляют собой данные. Или даже откуда взялся documentId. Насколько нам известно, documentId может быть нулевым или 0. Возможно, вы использовали var documentId=task.Document?.Id;

Panagiotis Kanavos 28.06.2024 16:16

@PanagiotisKanavos Да, я получаю documentId от task. Идентификатор существует, и после того, как я удалил Include из task, все заработало правильно, поэтому я и задал вопрос.

Web FR 28.06.2024 16:25

Публикуйте код, а не описывайте его. Вы можете думать, что вам это совершенно ясно, но мы не можем смотреть на ваш монитор или исходный код. Мы знаем, что EF Core 8 не поврежден, значит, что-то не так с приложением или данными.

Panagiotis Kanavos 28.06.2024 16:38

Похоже на проблему, если вы можете предоставить минимальный воспроизводимый пример, создайте проблему в репозитории EF Core.

Svyatoslav Danyliv 28.06.2024 17:05

@PanagiotisKanavos Я не правильно описал проблему, обновил вопрос, пожалуйста, посмотрите.

Web FR 28.06.2024 17:25
Document требуется свойство навигации?
Svyatoslav Danyliv 28.06.2024 17:55

@SvyatlavDanyliv Да, обязательно.

Web FR 28.06.2024 18:06

Спасибо за помощь, как оказалось, проблема была не в EF Core, а в сломанном конвейере посредника, на котором завязаны транзакции.

Web FR 01.07.2024 09:35
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это кажется странным. На первый взгляд похоже, что вы можете столкнуться с проблемой: в первом случае, когда вы загружаете документ как часть задачи, не загружая также тип документа и компоненты, тогда, возможно, средство отслеживания изменений EF могло вернуть этот отслеживаемый документ и проигнорировать. загрузка DocumentType и компонентов в соответствии с запросом во втором запросе...

Тем не менее, я создаю тест для проверки этого конкретного сценария, и EF по-прежнему активно загружает запрошенные данные во втором запросе.

В моем тестовом примере у меня была связь «Родитель->Дети->Комментарий», в которой я сначала запрашивал конкретного родителя и его детей, не загружая комментарии, а затем загружал дочерний элемент с его комментариями, которые были загружены. Я подозревал, что EF мог предоставить отслеживаемый дочерний элемент, поскольку он был загружен из первого запроса, без дальнейшей обработки Include, однако он перезагрузил дочерний элемент и связанный с ним комментарий.

var test = context.Parents
    .Include(x => x.Children)
    .Single(x => x.ParentId == 1); // No comment loaded.
var test2 = context.Children
    .Include(x => x.Comment)
    .First(x => x.ChildId == 1); // a child from parent #1

Если все, что вам нужно, это использовать DocumentId, было бы лучше просто спроецировать его и вообще избегать загрузки каких-либо объектов в кэш отслеживания:

var documentId = await _context.Tasks
    .where(t => t.Id == taskId)
    .Select(t => t.Document.ParentDocumentId.HasValue
         ? t.Document.ParentDocumentId.Value 
         : t.Document.Id)
    .SingleAsync();

Похоже, это решит вашу проблему, но это не объясняет, почему ваш второй запрос не загружает связанные данные, если этот документ загружается с готовностью.

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

Одним из виновников, который следует проверить, является то, как объявляются ваши свойства навигации, чтобы убедиться, что они остаются простыми и понятными, без «умной» логики, особенно с коллекциями. Например:

// Document.DocumentType:

public virtual DocumentType DocumentType { get; protected set; }  

// DocumentType.Components:

public virtual ICollection<Component> Components { get; protected set; } = []; // or new List<Component>();

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

Спасибо за помощь, как оказалось, проблема была не в EF Core, а в сломанном конвейере посредника, на котором завязаны транзакции.

Web FR 01.07.2024 09:35

Другие вопросы по теме