由.Include()相关实体创建的内部联接的IQueryable Count()方法问题

时间:2015-08-17 12:45:43

标签: c# entity-framework inner-join iqueryable

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

在“查询”变量中生成的查询:

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语句强制的内部联接中。

更新2

我不知道它是否重要但是,EntityOne上的IdEntityRelated1是必需属性,加上它不是数据库上的外键,只是存储相关实体Id的Integer字段。 (我正在使用数据库优先的POCO课程)

我有另一个工作源,其中字段但它们是可空的整数而不是必需的。也许我不应该尝试做一个Include强制内部加入所需的关系?

2 个答案:

答案 0 :(得分:1)

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

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

第一次计数 ...

var wrongCount = query.Count();

... Include被忽略。没有理由执行它们,因为EF已被告知被引用的对象(EntityRelated1EntityRelated2是必需的,因此内连接应该找到相关的记录entities.EntityOne并跳过其余部分。Includes只会使查询更加昂贵而且不会影响结果。

您可以通过监视为计数执行的SQL来检查。当你只看query时,这不是SQL生成的!它可能只是归结为

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

因此,第一个计数会返回数据库中所有EntityOne个记录的正确计数。

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

答案 1 :(得分:0)

#include <memory> struct A { void foo(const int& i) { } }; template <typename F, class ...Args> void a_caller(A& a, F &&f, Args&& ...args) { (a.*f)(std::forward<Args>(args)...); } int main() { int i = 42; A a; a_caller(a, &A::foo, i); } 不是LINQ方法,是一个EntityFramework扩展,旨在进行急切加载而不是其他。 Include s are lost if the query shape changes

  

当您调用Include方法时,查询路径仅对返回的IQueryable T实例有效.Tu的IQueryable的其他实例和上下文本身不受影响。

具体而言,这意味着,例如Include d Include之上的聚合将会松开IQueryable<T>(这正是您所看到的)。

请参阅Tip 22 - How to make Include really IncludeIQueryable.Include() gets ignoredInclude in following query does not include really等等。