【问题标题】:EF Core NotMapped Getter with logic and using navigation properties - lazy-loading is not supported for detached entities具有逻辑和使用导航属性的 EF Core NotMapped Getter - 分离实体不支持延迟加载
【发布时间】:2020-01-22 10:14:44
【问题描述】:

我有一个模型Activity,它有一些导航属性。

我在使用 EF 时使用延迟加载代理来获取所有属性。

NotMapped 属性Status 使用导航属性ApprovalStatus

当获取状态为 true 的 LINQ 条件的活动时,它会引发错误

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);

【问题讨论】:

  • Activity 的声明是什么?

标签: c# entity-framework .net-core entity-framework-core


【解决方案1】:

我猜这是 EF Core 2.x。我建议避免使用这种类型的查询表达式,因为 EF Core 3 中的自动查询评估发生了变化,以阻止人们依赖它。我怀疑还有一些错误的边缘情况,例如您遇到的客户评估认为实体已分离的情况。

在性能方面,从长远来看,依赖延迟加载会受到伤害。您可能会考虑集中规则(如状态)的一种选择是将它们反映为域中的表达式/函数。这适用于布尔表达式,因此需要安排您的“状态”检查以断言特定组合:

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 子句,您不能轻易否定或构成额外的 OR 条件。例如,以下内容不起作用:

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

我进行了一番挖掘,看看是否有办法得到它,所以表达式可能是这样的:

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

通过静态扩展方法返回一个表达式,但我无法得到任何工作。可能有一些 Func/Expression 的黑魔法可以通过 Linq2Entity 发挥作用,但即使是这样,我也希望它看起来很可怕。

IMO 的最佳选择是使用可用属性根据需要编写表达式。即:

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

能够重用表达式可能很好,但以上将是最灵活和性能最好的选项。

【讨论】:

    猜你喜欢
    • 2020-12-02
    • 1970-01-01
    • 2014-03-26
    • 2011-08-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多