Выражение LINQ не может быть переведено. Либо переписать запрос в форме, которую можно перевести, либо переключиться на оценку клиента EF Core 3.1 - PullRequest
1 голос
/ 04 марта 2020

Я борюсь с этим уже четыре дня и никакого прогресса вообще нет. Имея запрос, который прекрасно работал до обновления до EF Core 3.1:

var equipments = await this.DbContext.ServContrObjStructEquipment
            .AsNoTracking()
            .Where(e => e.ServContrObjStruct.ServContrObjStructParent.ServContrObjStructParent.Objectuuid == sectionGuid)
            .Where(e => e.ServContrObjStructPlanEquipment.Select(x => x.PkServContrObjStructPlanEquipment).Contains(
                e.ServContrObjStructPlanEquipment.OrderByDescending(x => x.ServContrObjStructPlanVers.ActiveUntil ?? DateTime.MaxValue).ThenByDescending(x => x.ServContrObjStructPlanVers.ActiveFrom).ThenByDescending(x => x.ActiveFrom).Select(x => x.PkServContrObjStructPlanEquipment).FirstOrDefault()
                ))

Теперь он выдает исключение, которое говорит:

Выражение LINQ 'DbSet .Where ( s3 => s3.RecStatus == 1) .Where (s3 => EF.Property> ((EntityShaperExpression: EntityType: ServContrObjStructEquipment ValueBufferExpression: (ProjectionBindingExpression: Outer.Outer. && EF.Property> ((EntityShaperExpression: EntityType: ServContrObjStructEquipment ValueBufferExpression: (ProjectionBindingExpression: Outer. => s3.PkServContrObjStructPlanEquipment) .Contains ((MaterializeCollectionNavigation (navigation: Навигация: ServContrObjStructEquipment.ServContrObjStructPlanEquipment, подзапрос: DbSet .Where (s4 = >Exha.Ser=Shere) EntityType: ServContrObjSt ructEquipment ValueBufferExpression: (ProjectionBindingExpression: Outer.Outer.Outer) IsNullable: False), "PkServContrObjStructEquipment") = NULL && EF.Property> ((EntityShaperExpression: EntityType: ServContrObjStructEquipment ValueBufferExpression: (ProjectionBindingExpression: Outer.Outer.Outer)! IsNullable: Ложные ), "PkServContrObjStructEquipment") == EF.Property> (i, "FkServContrObjStructEquipment"))) .AsQueryable () .OrderByDescending (x => x.ServContrObjStructAntilers ?? ?? 31.12.9999 23:59:59) .ThenByDescending (x => x.ServContrObjStructPlanVers.ActiveFrom) .ThenByDescending (x => x.ActiveFrom) .Select (x => x.PkServContrObjStructefirOpp)) ()) не может быть переведено. Либо переписайте запрос в форме, которую можно перевести, либо переключитесь на оценку клиента явно, вставив вызов либо AsEnumerable (), AsAsyncEnumerable (), ToList (), либо ToListAsyn c (). См. https://go.microsoft.com/fwlink/?linkid=2101038 для получения дополнительной информации.

Мне известно, что в EF Core 3.0-3.1 произошли серьезные изменения, и теперь оценка клиента выполняется на верхнем уровне Select(). Хотя я бы предпочел не делать этого, я попытался обзвонить всех ToList(), AsEnumerable() et c. чтобы он работал, но это также не помогает: запрос сложный и многоуровневый, поэтому вызов ToList() в любой момент либо нарушает его, либо не приводит к получению связанных записей (из-за отложенной загрузки, я полагаю), которые затем необходимы для дальнейшего выполнения запроса.

Я также попытался разделить запрос на отдельные запросы, чтобы посмотреть, что там происходит, например:

var intermEquipments = await this.DbContext.ServContrObjStructEquipment
                .AsNoTracking()
                .Include(e => e.ServContrObjStructPlanEquipment)
             .Where(e=>e.ServContrObjStruct.ServContrObjStructParent.ServContrObjStructParent.Objectuuid == sectionGuid)
                .ToListAsync();

            var intermEquipments1 = await this.DbContext.ServContrObjStructEquipment
                .AsNoTracking()
                .Include(e => e.ServContrObjStructPlanEquipment)
                .Where(e => e.ServContrObjStruct.ServContrObjStructParent.ServContrObjStructParent.Objectuuid == sectionGuid)
                .Select(e => e.ServContrObjStructPlanEquipment.Select(x => x.PkServContrObjStructPlanEquipment))
                .ToListAsync();

            var intermEquipments2 = this.DbContext.ServContrObjStructEquipment
                .AsNoTracking()
                .Include(e => e.ServContrObjStructPlanEquipment)
                .Where(e => e.ServContrObjStruct.ServContrObjStructParent.ServContrObjStructParent.Objectuuid == sectionGuid)
                .Select(e => e.ServContrObjStructPlanEquipment
                    .OrderByDescending(x => x.ServContrObjStructPlanVers.ActiveUntil ?? DateTime.MaxValue)
                                .ThenByDescending(x => x.ServContrObjStructPlanVers.ActiveFrom)
                                    .ThenByDescending(x => x.ActiveFrom)
                                        .Select(x => x.PkServContrObjStructPlanEquipment).ToList());

, но он либо выдает мне исключение, говоря что

Лямбда-выражение, используемое внутри Include, недопустимо

или NullReference (поскольку некоторые связанные свойства не загружены, как я уже упоминал выше) или вообще не отображает записи .

Я знаком с EntityFramework, хотя я не эксперт. Есть ли способ переписать этот запрос, чтобы он мог быть переведен или просто сделать что-нибудь, чтобы он работал? Спасибо!

1 Ответ

0 голосов
/ 05 марта 2020

Это похоже на ошибку или какое-то критическое изменение в EF Core (неожиданный сюрприз). Сначала я подозревал, что использование DateTime.MaxValue в качестве EF Core имело проблемы с этим в прошлом, но это не так. По какой-то причине он делает исключение при выполнении подвыбора OrderBy внутри Contains. Одним из серьезных изменений EF Core 3 по сравнению с 2.2.6 было то, что Core 2 автоматически переключался на выражения на стороне клиента, где Core будет выдавать исключение, однако я смог подтвердить с помощью Profiler, что такой запрос был успешно переведен в SQL с Core 2.2.6, но в 3.1 он не переводится, что приводит к ошибке. На этом этапе вы можете создать отчет об ошибке с командой EF Core.

Мне удалось воспроизвести это с помощью более простого запроса:

var results = context.Parents.Where(x => x.Children.Select(c => c.ChildId)
   .Contains(x.Children.OrderByDescending(c => c.BirthDate).Select(c => c.ChildId).FirstOrDefault()))
   .ToList();

Запустите в 2.2.6, он успешно компилируется в SQL. 3.1 проверяет исключение.

Вероятно, было бы полезно точно понять, что должен возвращать этот запрос, потому что я прочитал его 3 раза, и он все еще не имеет смысла. :)

Вы выбираете ServerEquipment, где их Struct.Paremt.Parent.ObjectID = ID секции И их план оборудования содержат, насколько я могу судить, самую раннюю запись оборудования в этом наборе. Эта проверка Contains не имеет смысла, потому что она просто ищет существование выбранной строки в этом наборе без каких-либо внешних критериев? Я имею в виду, что все это выглядит как .Where(x => x.Children.Select(c => c.ChildId).Contains(x.Children.OrderByDescending(c2 => c2.BirthDate).Select(c2 => c2.ChildId).FirstOrDefault()), который является довольно причудливым способом тратить время базы данных. Т.е. там, где содержатся дети старшего ребенка. (Ну, конечно, это так.) Так что я могу только предположить, что я что-то там упустил, но я действительно не хотел бы пытаться подобрать структуру сущностей и запросы, подобные этим 6 месяцев спустя ..:)

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

...