.Include() 相关实体创建的 Inner Join 的 IQueryable Count() 方法问题

IQueryable Count() method issue with Inner Join created by .Include() related entities

IQueryable<EntityOne> query = entities.EntityOne
    .Include(t => t.EntityRelated1)
    .Include(t => t.EntityRelated2)
    .AsQueryable();

在 "query" 变量中生成的查询:

SELECT 
[Extent1].[Id] AS [IdEntityOne], 
 ...
[Extent2].[Id] AS [IdEntityRelatedOne], 
...
[Extent3].[Id] AS [IdEntityRelatedTwo], 
...
FROM   [dbo].[EntityOne] AS [Extent1]
   INNER JOIN [dbo].[EntityRelatedOne] AS [Extent2]
       ON [Extent1].[IdRelatedOne] = [Extent2].[Id]
   INNER JOIN [dbo].[EntityRelatedTwo] AS [Extent3]
       ON [Extent1].[IdRelatedTwo] = [Extent3].[Id]

之后,在 C# 代码上,这些是计数的结果:

var wrongCount = query.Count(); // Returns 295
var correctCount = query.AsEnumerable().Count(); // Returns 10

295 计数是完整的 EntityOne 集寄存器数。 (错误)

10 计数是内部联接后所需的计数。

听起来 IQueryable.Count() 在对数据库执行 InnerJoin 之前正在计数。我不想生成 IEnumerable,因为我希望在 Sql Server 上与内部联接一起执行计数。

更新 1

正在尝试手动执行内连接:

IQueryable<EntityOne> query2 = entities.EntityOne.Join(entities.EntityTwo,
     eone=> eone.IdRelatedOne, en => en.Id,
     (x, y) => x);

在"query2"中生成的SQL代码是:

SELECT 
[Extent1].[Id] AS [Id], 
...
FROM [dbo].[EntityOne] AS [Extent1]

如您所见,相关实体未包含在 linq Join 语句强制的 Inner Join 中。

更新 2

我不知道这是否重要,但是 EntityOne 上的 IdEntityRelated1 是必需的 属性,而且它不是数据库上的外键,只是一个存储相关实体 ID 的整数字段。 (我使用数据库优先的 POCO 类)

我有另一个工作来源,其中字段但它们是可为空的整数而不是必需的。也许我不应该尝试执行包含以强制所需关系之间的内部连接?

Include 不是一个正确的 LINQ 方法,是一个 EntityFramework 扩展,旨在进行预先加载而不是其他。 Includes are lost if the query shape changes:

When you call the Include method, the query path is only valid on the returned instance of the IQueryable of T. Other instances of IQueryable of T and the context itself are not affected.

具体来说,这意味着,例如 Included IQueryable<T> 之上的聚合将失去 Include(这正是您所看到的)。

查看 Tip 22 - How to make Include really Include, IQueryable.Include() gets ignored, Include in following query does not include really 等等。

您有一个必需的关联,但数据库中不存在预期的对象。

但让我们先看看 EF 做了什么。

第一次计数...

var wrongCount = query.Count();

...Include 被忽略。没有理由执行它们,因为 EF 已被告知引用的对象(EntityRelated1EntityRelated2 是强制性的,因此内连接 有望找到相关记录 . 如果他们这样做,EF 认为它可能只计算 entities.EntityOne 并跳过其余部分。Includes 只会使查询更昂贵并且它们不会影响结果。

您可以通过监控为计数执行的 SQL 来检查。那不是你只看query时生成的SQL!这可能简单地归结为

SELECT COUNT(*) FROM [dbo].[EntityOne]

因此第一个计数 return 是数据库中所有 EntityOne 条记录的正确计数。

对于 秒数 ,您强制执行存储在 query 变量中的整个查询,即您显示的 SQL 语句。然后你在内存中计算它的结果——它 returns 10。这意味着使用内部连接的查询实际上有 return 10 条记录。反过来,这只能意味着一件事:有 285 个 EntityOne.IdRelatedOne 值没有指向现有的 EntityRelatedOne 记录。但是您根据需要映射了关联,因此 EF 生成了内部联接。外连接也会 return 295.