Entity Framework Core带有过滤器的Include

5
我正在尝试从数据库中获取一个对象列表,这些对象都有一个子对象列表。
以下是示例:
 public class User 
 {
    public int Id { get; set; }
    public ICollection<Child> Childs { get; set; }
 }

 public class Child 
 {
    public int Id { get; set; }
    public string UserId { get; set; }
    public User User { get; set; }
 }

这里的问题是我找不到一种同时筛选孩子条件和带出用户列表的方法。
我尝试了以下代码:
users = _context.Users.Where(e => e.Childs.Any(ec => ec.Id > 1))

但是,如果不满足条件,它将不会返回用户,我希望所有用户都能返回,即使他们没有子项或条件未满足。
此外,我发现了这个项目:https://github.com/zzzprojects/EntityFramework-Plus,但是似乎它不支持我想要做的EF Core。
有人有什么建议吗?

2
过滤包含 - 在所有EF版本中都不支持,包括Core。在SO上经常被问到的问题。 - Ivan Stoev
@IvanStoev 感谢您的评论,但那并没有回答我的问题。您知道有什么替代方法吗? - legollas007
1
只有通过一些明确的加载/导航属性修复相关技巧,类似于文档中的您还可以过滤要加载到内存中的相关实体示例。但这需要查询被实现。 - Ivan Stoev
最终,EF Core 5 在 Include 中实现了过滤功能。MSDN 文档:https://learn.microsoft.com/en-us/ef/core/querying/related-data#filtered-include - Mofaggol Hoshen
1个回答

10

免责声明: 我是项目Entity Framework Plus的所有者。

由于存在N+1查询问题,我们的库目前还不支持.NET Core的查询过滤器。

在我们的EF6底层中,我们的库只执行了一个简单的投影操作。

类似于使用您的信息执行以下操作:

var users = _Context.Users.Select(x => new {
                Users = x,
                Childs = x.Childs.Any(ec => ec.Id > 1)
            })
            .ToList()
            .Select(x => x.Users)
            .ToList();

然而,对于 EF Core 来说,相同的投影会为每个用户获取一个子级别的数据库往返(N+1 queries)。

您可以尝试以下投影,并查看是否会出现相同的错误。

我认为,在 EF Core 团队修复此问题之前,据我所知,没有方法可以过滤子实体。

SELECT [x].[Id], [x].[ColumnInt]
FROM [Lefts] AS [x]
go
exec sp_executesql N'SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [Rights] AS [ec1]
        WHERE ([ec1].[Id] > 1) AND (@_outer_Id1 = [ec1].[LeftId]))
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END',N'@_outer_Id1 int',@_outer_Id1=1
go
exec sp_reset_connection
go
exec sp_executesql N'SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [Rights] AS [ec1]
        WHERE ([ec1].[Id] > 1) AND (@_outer_Id1 = [ec1].[LeftId]))
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END',N'@_outer_Id1 int',@_outer_Id1=2
go
exec sp_reset_connection
go
exec sp_executesql N'SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [Rights] AS [ec1]
        WHERE ([ec1].[Id] > 1) AND (@_outer_Id1 = [ec1].[LeftId]))
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END',N'@_outer_Id1 int',@_outer_Id1=3
go
exec sp_reset_connection
go
exec sp_executesql N'SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [Rights] AS [ec1]
        WHERE ([ec1].[Id] > 1) AND (@_outer_Id1 = [ec1].[LeftId]))
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END',N'@_outer_Id1 int',@_outer_Id1=4
go
exec sp_reset_connection
go
exec sp_executesql N'SELECT CASE
    WHEN EXISTS (
        SELECT 1
        FROM [Rights] AS [ec1]
        WHERE ([ec1].[Id] > 1) AND (@_outer_Id1 = [ec1].[LeftId]))
    THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT)
END',N'@_outer_Id1 int',@_outer_Id1=5
go

回答子问题:

随着.NET Core 2.0的新发布,这个问题是否已经得到解决了?

不幸的是,Entity Framework在V2.x版本中仍然不能正确处理类型转换。

例如,使用Cast方法的这个LINQ查询无法正常工作:

var ids = ctx.MyTables
    .Cast<IMyTable>()
    .Cast<MyTable>()
    .Where(x => x.SomeKey.Equals(keyId))
    .Select(x => x.MyFieldIntegerIWant)
    .ToList();

编辑:更新状态#1

不幸的是,EF Core团队仍未解决关于投影的N+1查询问题。

根据对v3.0最新状态的了解,我认为他们也没有计划很快修复这种问题:https://github.com/aspnet/EntityFrameworkCore/issues/10001#issuecomment-456581915

编辑:更新状态#2

现在,IncludeFilter功能已经在EF Plus的最新版本(EF Core 2.x和EFCore 3.x)中得到支持。


随着.NET Core 2.0的新版本发布,这个问题是否得到了解决? - legollas007
1
请看我添加的注释,简而言之,不……2.0版本仍然存在同样的问题。 - Jonathan Magnan
1
如果他们修复了这个问题,请回来更新这个答案! - MikeT
这对于过于复杂的关系型数据库以及一般使用EF对象的工作来说将是非常惊人的。 - Chris Go
这已经被修复了!@JonathanMagnan,是否有任何更新? - cyptus
1
是的,@cyptus,我们的库已经支持它几周了。 - Jonathan Magnan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接