EF Core NotMapped Getter с logi c и использованием свойств навигации - отложенная загрузка не поддерживается для отдельных объектов - PullRequest
0 голосов
/ 21 января 2020

У меня есть модель Activity, у которой есть некоторые навигационные свойства.

Я использую ленивые загрузочные прокси для извлечения всех свойств при использовании EF.

Свойство NotMapped Status использует свойство навигации ApprovalStatus.

При извлечении действий с условием LINQ о том, где статус имеет значение true, выдается ошибка

System.InvalidOperationException: ошибка, сгенерированная для предупреждения 'Microsoft .EntityFrameworkCore.Infrastructure.DetachedLazyLoadingWarning: Была предпринята попытка лениво загрузить свойство навигации «ApprovalStatus» в отдельную сущность типа «ActivityProxy». Ленивая загрузка не поддерживается для отдельных сущностей или сущностей, которые загружены с помощью AsNoTracking ().

Модель:

public class Activity {

    ...

    public virtual ICollection<Approver> ApprovalStatus {get; set;}


    [NotMapped]
    public bool? Status {
        get {
            if (ApprovalStatus.Any(x => ...) return false;
            if (ApprovalStatus.All(x => ...) return true;

            return null;
        }
    }
}

Запрос, который я пытаюсь выполнить выполнить:

applicationDbContext.Activities.Where(a => a.Status == true);

Но если я преобразовываю действия в список до выполнения условия, оно работает.

applicationDbContext.Activities.ToList().Where(a => a.Status == true);

1 Ответ

0 голосов
/ 22 января 2020

Я предполагаю, что это EF Core 2.x. Я бы порекомендовал избегать этого типа выражения запроса, так как автоматическая оценка запросов c была изменена в EF Core 3, чтобы люди не полагались на него. Я подозреваю, что существуют также крайние случаи с ошибками, например, то, с чем вы столкнулись, когда выполняемая клиентская оценка думает, что сущность отсоединена.

С точки зрения производительности, полагаться на ленивую загрузку будет вредно в долгосрочной перспективе. Один из вариантов, который вы могли бы рассмотреть для централизации правил, таких как Status, состоял бы в том, чтобы отразить их как выражения / функции в домене. Это работает с булевыми выражениями, поэтому ваша проверка «Статус» должна быть организована так, чтобы утверждать определенную комбинацию:

public class Activity {

    ...

    public virtual ICollection<Approver> ApprovalStatus {get; set;}

    public static Func<Activity, bool> IsApproved() {
       return (Activity a) => (a.ApprovalStatus.All(x => ...) 
           && !a.ApprovalStatus.Any(x => ...);

    public static Func<Activity, bool> IsRejected() {
       return (Activity a) => (!a.ApprovalStatus.All(x => ...) 
           || a.ApprovalStatus.Any(x => ...);

    }
}

, затем при потреблении:

applicationDbContext.Activities.Where(Activity.IsApproved());

Вы все еще можете иметь неотображенный свойство для отложенной оценки в коде:

[NotMapped]
public bool? Status
{
    get 
    { 
        if(IsApproved().Invoke(this))
            return true;
        if(IsRejected().Invoke(this))
            return false;
        return null;
    }
}

Предостережение этого подхода состоит в том, что с вашими предложениями Where вы не можете легко отрицать или составлять дополнительные условия ИЛИ. Например, следующее не работает:

applicationDbContext.Activities.Where(!Activity.IsApproved());

Мне пришлось немного покопаться, чтобы посмотреть, есть ли способ получить его, чтобы выражение могло быть примерно таким:

applicationDbContext.Activities.Where(x => x.IsApproved());

с помощью метода расширения stati c чтобы вернуть выражение, но я не смог заставить что-либо работать. Там может быть какая-то черная магия Func / Expression c, которая сработает и на самом деле сделает это через Linq2Entity, но даже если так, я ожидаю, что это будет выглядеть довольно отвратительно.

Лучший вариант IMO - это написать выражения как вам нужно с доступными свойствами. Т.е.:

applicationDbContext.Activities.Where(a => (a.ApprovalStatus.All(x => ...) 
       && !a.ApprovalStatus.Any(x => ...)
       // ...
);

Возможно, было бы неплохо иметь возможность повторно использовать выражения, но приведенный выше вариант был бы наиболее гибким и наиболее эффективным.

...