При переходе с 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);
@PanagiotisKanavos Да, я получаю documentId от task. Идентификатор существует, и после того, как я удалил Include из task, все заработало правильно, поэтому я и задал вопрос.
Публикуйте код, а не описывайте его. Вы можете думать, что вам это совершенно ясно, но мы не можем смотреть на ваш монитор или исходный код. Мы знаем, что EF Core 8 не поврежден, значит, что-то не так с приложением или данными.
Похоже на проблему, если вы можете предоставить минимальный воспроизводимый пример, создайте проблему в репозитории EF Core.
@PanagiotisKanavos Я не правильно описал проблему, обновил вопрос, пожалуйста, посмотрите.
Document требуется свойство навигации?
@SvyatlavDanyliv Да, обязательно.
Спасибо за помощь, как оказалось, проблема была не в EF Core, а в сломанном конвейере посредника, на котором завязаны транзакции.





Это кажется странным. На первый взгляд похоже, что вы можете столкнуться с проблемой: в первом случае, когда вы загружаете документ как часть задачи, не загружая также тип документа и компоненты, тогда, возможно, средство отслеживания изменений 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, а в сломанном конвейере посредника, на котором завязаны транзакции.
EF Core не сломан. Мы не можем угадать, что на самом деле делает ваш код или что представляют собой данные. Или даже откуда взялся
documentId. Насколько нам известно,documentIdможет быть нулевым или 0. Возможно, вы использовалиvar documentId=task.Document?.Id;