C # EF Core 5.0 GroupBy Select Conditional Count не может быть переведен

У меня есть наблюдение за отдельными видами и группа наблюдений, где существует связь «многие ко многим» (IndividualObservationGroup). Теперь я получаю сообщение об ошибке в следующем коде, так как я перешел с efcore2.1 на efcore5.0. Я получаю сообщение об ошибке:

System.InvalidOperationException: выражение LINQ 'GroupByShaperExpression: KeySelector: i.ObservationGroupGuid, ElementSelector: EntityShaperExpression: EntityType: IndividualObservationGroup ValueBufferExpression: ProjectionBindingExpression: EmptyProjectionMember IsNullable: False

    .Count(x => x.IndividualObservation.SexCodeId == (Nullable<int>)1)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.

Вот код:

var queryable = context.IndividualObservationGroup.AsNoTracking()
                       .Include(obg => obg.ObservationGroup)
                       .Include(obg => obg.IndividualObservation)
                           .ThenInclude(io => io.SetDetail)
                       .Where(items => String.Equals(items.ObservationGroupGuid, lookupItem.ObservationGroupGuid) &&
                                                     items.GroupNumber == lookupItem.GroupNumber &&
                                                     items.TargetItem == lookupItem.TargetItem)
                                            .GroupBy(items => items.ObservationGroupGuid)
                                            .Select(items => new
                                            {
                                                TallyKey = items.Key,
                                                Females = items.Count(x => x.IndividualObservation.SexCodeId == 1),
                                                Males = items.Count(x => x.IndividualObservation.SexCodeId == 2),
                                                UnSexed = items.Count(x => x.IndividualObservation.SexCodeId == 3),
                                                numberFromCurrentSet = items.Count(x => x.IndividualObservation.ParentGuid == setDetailGuid)
                                            });
var group = await queryable.FirstOrDefaultAsync();

person fradsham    schedule 22.02.2021    source источник
comment
проверьте эта статья   -  person Magnetron    schedule 22.02.2021
comment
Спасибо за информацию. Я уже просматривал статью раньше. Я знаю, что это можно записать как оператор sql и запустить как FromSqlRaw в базе данных, но я предпочитаю не использовать конкретный оператор SQL, который может заблокировать меня в конкретной базе данных (основная причина, по которой я выбрал ядро ​​ef). Также я предпочитаю не передавать записи клиенту (AsEnumerable), что снижает производительность.   -  person fradsham    schedule 23.02.2021
comment
Если я использую только items.Count (), то запрос выполняется в базе данных, но с добавлением любого лямбда-выражения, например. items.Count (x = ›x.IndividualObservation.SexCodeId == 3) или items.Count (x =› x.IndividualObservation.ParentGuid == setDetailGuid) вызывает исключение из-за невозможности перевода. Просто пытаюсь понять почему.   -  person fradsham    schedule 23.02.2021
comment
Вы уже отправили записи клиенту в 2.1, вы просто не знали об этом. Это то, что сделали перерывные изменения в 3.0, это больше не откат к оценке на стороне клиента, вы должны сделать это явным образом.   -  person Magnetron    schedule 23.02.2021
comment
Я не знал этого, пока не перешел с версии 2.1 на 5.0. Если я удалю лямбда-операторы в операторах items.Count () (что считается логически неверным), ядро ​​ef 5.0 будет запускать его без исключения. Но добавление к нему лямбды вызывает исключение: не удается перевести. Итак, items.Count () работает, а items.Count (x = ›x.IndividualObservation.ParentGuid == setDetailGuid) - нет.   -  person fradsham    schedule 23.02.2021


Ответы (1)


После получения отзывов с сайта efcore github (https://github.com/dotnet/efcore/issues/24238) Было рекомендовано ввести соединение между таблицами, хотя я указал это в коде контекста базы данных построителя моделей:

modelBuilder.Entity<IndividualObservation>(entity =>
            {
                entity.HasKey(x => x.GUID);
                entity.HasMany(x => x.ObservationGroups)
                       .WithOne(e => e.IndividualObservation)
                       .HasForeignKey(e => e.IndividualObservationGuid);
            });
modelBuilder.Entity<ObservationGroup>(entity =>
            {
                entity.HasKey(x => x.GUID);
                entity.HasIndex(x => x.GUID);
                entity.HasMany(x => x.IndividualObservations)
                       .WithOne(e => e.ObservationGroup)
                       .HasForeignKey(e => e.ObservationGroupGuid);
            });
modelBuilder.Entity<IndividualObservationGroup>(entity =>
            {
                entity.HasKey(io => new { io.IndividualObservationGuid, io.ObservationGroupGuid });
            });

Причина была такова: в настоящее время EF Core не расширяет навигацию после применения оператора GroupBy. Вы используете навигацию IndividualObservation, поэтому она не работает. В качестве временного решения вы можете развернуть навигацию вручную с помощью соединения перед применением GroupBy, и это должно сработать.

Итак, я добавил следующие объединения, и это работает:

var queryable = context.IndividualObservationGroup.AsNoTracking()
                       .Include(obg => obg.ObservationGroup)
                       .Include(obg => obg.IndividualObservation)
                           .ThenInclude(io => io.SetDetail)
                       .Join(context.ObservationGroup,
                                 iog => iog.ObservationGroupGuid,
                                 og => og.GUID,
                                 (iog, og) => new {
                                     IndividualObservationGuid = iog.IndividualObservationGuid,
                                     GroupNumber = iog.GroupNumber,
                                     TargetItem = iog.TargetItem,
                                     ObservationGroupGuid = og.GUID
                            })
                        .Join(context.IndividualObservation,
                                     iog => iog.IndividualObservationGuid,
                                     io => io.GUID,
                                     (iog, io) => new {
                                           ObservationGroupGuid = iog.ObservationGroupGuid,
                                           GroupNumber = iog.GroupNumber,
                                           TargetItem = iog.TargetItem,
                                           IndividualObservationGuid = io.GUID,
                                           SetDetailGuid = io.ParentGuid,
                                           SexCodeId = io.SexCodeId
                              })
                       .Where(items => String.Equals(items.ObservationGroupGuid, lookupItem.ObservationGroupGuid) &&
                                                     items.GroupNumber == lookupItem.GroupNumber &&
                                                     items.TargetItem == lookupItem.TargetItem)
                       .GroupBy(items => items.ObservationGroupGuid)
                       .Select(items => new
                                     {
                                        TallyKey = items.Key,
                                        Females = items.Count(x => x.SexCodeId == 1),
                                        Males = items.Count(x => x.SexCodeId == 2),
                                        UnSexed = items.Count(x => x.SexCodeId == 3),
                                        numberFromCurrentSet = items.Count(x => x.SetDetailGuid == setDetailGuid)
                                       });
var group = await queryable.FirstOrDefaultAsync();

Итак, добавление двух операторов соединения позволило мне избежать требования IEnumerable и выполнить запрос на клиенте. Просто надейтесь, что когда-нибудь в будущем efcore сможет использовать больше преимуществ настройки соединений в операторе конструктора моделей и использовать его для IQueryable, помимо навигации.

person fradsham    schedule 24.02.2021