首页 > 解决方案 > EF Core 5:Include With Where 语句后跟 ThenInclude——继承问题

问题描述

我有一个正在开发的项目。首先,模式非常简单;我有一个用户,这个用户在很多仓库工作。有不同类型的仓库,但是——对于这个例子——我会保持简单。在此示例中,我们有一个继承自 Warehouse 的 ManagedWarehouse。

以下是课程:

public class User
{
       public int UserId { get; set; }
       public string Name { get; set; }
       public List<Warehouse> Warehouses { get; set; }
}

public class Warehouse
{
     public int WarehouseId { get; set; }
     public bool IsActive { get; set; }
}

public class ManagedWarehouse : Warehouse
{
     public int GeneralManagerId { get; set; }
     public User GeneralManager { get; set; }
}

现在我正在创建一个 EF 查询,它允许我获取所有用户以及他们工作的 ACTIVE 仓库。另外,如果仓库是a ManagedWarehouse,也会退回GeneralManager。

这是我的查询:

public IQueryable<User> GetUsers()
{
     return this.DbContext.Users.Include(u => u.Warehouses.Where(wh => wh.IsActive)).ThenInclude(wh => ((ManagedWarehouse)wh).GeneralManager);
}

这似乎是一个简单的查询,但它不会编译。它抱怨这个电话是模棱两可的:

严重性代码 描述 项目文件行抑制状态错误 CS0121 调用在以下方法或属性之间不明确: 'EntityFrameworkQueryableExtensions.ThenInclude<TEntity, TPreviousProperty, TProperty>(IIncludableQueryable<TEntity, IEnumerable>, Expression<Func<TPreviousProperty, TProperty>>) ' 和 'EntityFrameworkQueryableExtensions.ThenInclude<TEntity, TPreviousProperty, TProperty>(IIncludableQueryable<TEntity, TPreviousProperty>, Expression<Func<TPreviousProperty, TProperty>>)'

如果我删除.Where(wh => wh.IsActive),它会编译,但是 - 再次 - 我只希望返回活动的仓库。如何在返回所需数据的同时使其“明确”?谢谢你。

标签: entity-framework-core

解决方案


EF CoreThenInclude模式从一开始就存在 C# IntelliSense/编译器的问题,因为定义了两个重载 - 一个 forT和一个 for IEnumerable<T>. 由于这两个重载都适用,看起来编译器只有在您使用 的某些直接属性/方法时才能推断出正确的重载T,否则(参数上的任何其他运算符,包括as问题中的强制转换等)它会失败。

这是最小的可重现示例:


class A
{
    public IEnumerable<B> Bs { get; set; }
}

class B
{
    public C C { get; set; }
}

class C
{
}

static void Test(IQueryable<A> source)
{
    var includable = source
        .Include(a => a.Bs);
    var compiles = includable.ThenInclude(b => b.C);
    var doesNotCompile = includable.ThenInclude(b => ((B)b).C); // CS0121
}

强制转换是多余的,因为btype is B,但它破坏了编译器。有趣的是,这只发生在前一个表达式返回确切的接口(IEnumerable<T>在这种情况下)时 - 如果表达式是派生类/接口(在这种情况下IReadOnlyCollection<TList<T>等),编译器推断有效。

这就是ThenInclude过滤失败的原因Include,因为它返回IEnumerable<T>,即使Include如果集合导航属性类型是IEnumerable<T>(由 EF Core 支持)而不是通常的ICollection<T>或其他派生类型,它也会发生。

话虽如此,我不确定这是否应该被视为(并报告)为 EF Core API 设计错误或 C# 编译器问题。但无论哪种情况,我认为它不会很快得到解决。因此,作为一种解决方法,让编译器通过显式指定参数类型来识别所需的重载:

.ThenInclude((Warehouse wh) => ((ManagedWarehouse)wh).GeneralManager)
//               ^^^

推荐阅读