Entity Framework中Queryable Linq查询的差异

5
我在实体框架中有一个非常简单的多对多表格,将我的批准与我的交易连接起来(如下图所示)。 enter image description here 我正在尝试在批准对象内部进行查询,以计算批准上的交易数量,这应该相对容易。
如果我像这样做,它就会非常快速。
int count;
EntitiesContainer dbContext = new EntitiesContainer ();

var aCnt = from a in dbContext.Approvals
        where a.id == id
        select a.Transactions.Count;

count = aCnt.First();

然而,当我这样做时,

(然而当我这样做时)

count = Transactions.Count;

或者这个

count = Transactions.AsQueryable<Transaction>().Count();

它非常缓慢。我已经追踪了服务器上运行的SQL,它确实似乎试图加载所有交易而不仅仅是在交易集合上执行COUNT查询。
有人能解释一下为什么吗?
附加:
以下是这两个类相关的EF模型的样子
更新:
感谢所有回答,我相信我错在相信连接到Approval对象的集合将作为IQueryable执行。我将不得不对dbContext对象执行计数。
谢谢大家。

在这个上下文中,Transactions的类型是什么:count = Transactions.Count; - romanoza
@GrantWinney 在我的Approval对象的ToString()函数中,我会输出附加的交易数量。我可以使用这三个片段中的任何一个。 - Christopher Townsend
@RomanKo Transactions是我正在编辑的Approval对象的ICollection的一部分。实体框架捕捉到了外键关系并默认创建了它。 - Christopher Townsend
Transactions对象的实际类型是什么?也就是说,调用Transactions.GetType()并告诉我们类型名称。 - Jean Hominal
请参见上文,这只是一个Transaction对象。EntityFramework会创建一个ICollection对象来将它们存储在Approval对象中。 - Christopher Townsend
4个回答

5
var aCnt = from a in dbContext.Approvals
    where a.id == id
    select a.Transactions.Count;

EF会自行编译查询,上述查询将被编译为select count transactions。

不同于此,

count = Transactions.AsQueryable<Transaction>().Count(); 
count = Transactions.Count;

这将从交易记录中选择所有记录,然后计算数量。

我认为当调用IQueryable方法时,它只执行SQL查询? - Christopher Townsend
1
IQueryable 方法返回 Enumerable 类型,因此它将首先获取所有记录,然后计算计数。 - Dev
这听起来不太对,我认为 IQueryable 方法的强大之处在于它是在数据库上查询而不是作为一个可枚举对象? - Christopher Townsend
@ChristopherTownsend:使用AsQueryable仅在基础对象实际实现IQueryable时才启用延迟执行/分析 - 否则,AsQueryable仅创建一个视图,将操作转发到基础IEnumerable - Jean Hominal

1
你的第一种方法允许在数据库服务器级别上进行计数。它将要求数据库不返回记录,而是返回找到的记录数量。这是最有效的方法。
这并不是说其他方法不能同样有效,但是在另外两行代码中,你首先没有明确表明你正在从Approvals的连接中检索交易。相反,在另外两行代码中,你仅仅获取Transactions集合并对其进行计数,基本上强制填充集合以便进行计数。

我明白了,但是它不应该看到交易集合与我所在的批准对象相连接吗? - Christopher Townsend
1
它应该如何看待?我在你的代码中没有看到你使用任何对“Approval”对象的引用...就像第一个查询中没有在“Transactions”前面加上“a.”。 - Arwin

1
你的第一个代码片段会在数据库服务器上执行查询。这是因为IQueryable实例是由Entity Framework提供的ObjectQuery类型,它执行必要的SQL翻译和执行。
第二个代码片段演示了如何使用IEnumerable实例。 Count()方法通过枚举整个集合来计算数量。
在第三个代码片段中,你试图再次将IEnumerable转换为IQueryable。但是Enumerable.AsQueryable方法无法知道它获取的IEnumerable是来自Entity Framework。它能做的最好的事情就是将IEnumerable包装在一个EnumerableQuery实例中,该实例仅动态编译给定给所有LINQ查询运算符的表达式树并在内存中执行它们。
如果您需要数据库服务器计算计数,您可以手动制定必要的查询(即在片段一中编写的内容),或者如果您没有使用Code First,则可以使用方法CreateSourceQuery。请注意,它将在数据库服务器上真正执行,因此如果您已修改集合并尚未保存更改,则结果将与直接调用Count返回的结果不同。

1
当您访问a.Transactions属性时,将加载交易列表(延迟加载)。如果您只想获取Count,请使用以下内容:
dbContext.Transactions.Where(t => t.Approvals.Any(ap => ap.Id == a.Id)).Count();

其中a表示已给予批准。


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