.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 扩展,旨在进行预先加载而不是其他。 Include
s 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.
具体来说,这意味着,例如 Include
d 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 已被告知引用的对象(EntityRelated1
和 EntityRelated2
是强制性的,因此内连接 有望找到相关记录 . 如果他们这样做,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.
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 扩展,旨在进行预先加载而不是其他。 Include
s 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.
具体来说,这意味着,例如 Include
d 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 已被告知引用的对象(EntityRelated1
和 EntityRelated2
是强制性的,因此内连接 有望找到相关记录 . 如果他们这样做,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.