[英]Entity Framework COUNT is doing a SELECT of all records
Profiling my code because it is taking a long time to execute, it is generating a SELECT instead of a COUNT and as there are 20,000 records it is very very slow. 对我的代码进行性能分析,因为要花很长时间才能执行,它会生成SELECT而不是COUNT,并且由于有20,000条记录,因此非常慢。
This is the code: 这是代码:
var catViewModel= new CatViewModel();
var catContext = new CatEntities();
var catAccount = catContext.Account.Single(c => c.AccountId == accountId);
catViewModel.NumberOfCats = catAccount.Cats.Count();
It is straightforward stuff, but the code that the profiler is showing is: 它是简单明了的东西,但探查器显示的代码是:
exec sp_executesql N'SELECT
[Extent1].xxxxx AS yyyyy,
[Extent1].xxxxx AS yyyyy,
[Extent1].xxxxx AS yyyyy,
[Extent1].xxxxx AS yyyyy // You get the idea
FROM [dbo].[Cats] AS [Extent1]
WHERE Cats.[AccountId] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=7
I've never seen this behaviour before, any ideas? 我以前从未见过这种行为,有什么想法吗?
Edit: It is fixed if I simply do this instead: 编辑:它是固定的,如果我只是这样做:
catViewModel.NumberOfRecords = catContext.Cats.Where(c => c.AccountId == accountId).Count();
I'd still like to know why the former didn't work though. 我仍然想知道为什么前者不起作用。
So you have 2 completely separate queries going on here and I think I can explain why you get different results. 因此,您在这里进行了2个完全独立的查询,我想我可以解释为什么您得到不同的结果。 Let's look at the first one 让我们看第一个
// pull a single account record
var catAccount = catContext.Account.Single(c => c.AccountId == accountId);
// count all the associated Cat records against said account
catViewModel.NumberOfCats = catAccount.Cats.Count();
Going on the assumption that Cats
has a 0..*
relationship with Account
and assuming you are leveraging the frameworks ability to lazily load foreign tables then your first call to catAccounts.Cats
is going to result in a SELECT
for all the associated Cat
records for that particular account. 继续假设Cats
与Account
之间的关系为0..*
,并假设您正在利用框架功能延迟加载外部表,然后您对catAccounts.Cats
的首次调用将导致对所有与Cat
相关的Cat
记录进行SELECT
该特定帐户。 This results in the table being brought into memory therefore the call to Count()
would result in an internal check of the Count
property of the in-memory collection (hence no COUNT
SQL generated). 这导致表被带入内存,因此对Count()
的调用将导致对内存中集合的Count
属性的内部检查(因此不会生成COUNT
SQL)。
The second query 第二个查询
catViewModel.NumberOfRecords =
catContext.Cats.Where(c => c.AccountId == accountId).Count();
Is directly against the Cats
table (which would be IQueryable<T>
) therefore the only operations performed against the table are Where
/ Count
, and both of these will be evaluated on the DB-side before execution so it's obviously a lot more efficient than the first. 直接针对Cats
表(这将是IQueryable<T>
),因此对该表执行的唯一操作是Where
/ Count
,并且这两个操作都将在执行执行之前在DB端进行评估,因此显然效率比首先。
However, if you need both Account
and Cats
then I would recommend you eager load the data on the fetch, that way you take the hit upfront once 但是,如果您同时需要Account
和Cats
那么我建议您急于将数据加载到提取中,这样您就可以先进行一次点击
var catAccount = catContext.Account.Include(a => a.Cats).Single(...);
Most times, when somebody accesses a sub-collection of an entity, it is because there are a limited number of records, and it is acceptable to populate the collection . 大多数情况下,当某人访问实体的子集合时,这是因为记录数量有限,并且可以填充该集合 。 Thus, when you access: 因此,当您访问:
catAccount.Cats
(regardless of what you do next), it is filling that collection . (无论您下一步要做什么),它都会填充该集合 。 Your .Count()
is then operating on the local in-memory collection. 然后,您的.Count()
将在本地内存集合上运行。 The problem is that you don't want that. 问题是您不想要那样。 Now you have two options: 现在,您有两个选择:
I'm pretty confident that if you did: 我非常有信心,如果您这样做:
catViewModel.NumberOfRecords =
catContext.Cats.Count(c => c.AccountId == accountId);
it will work just fine. 它会很好地工作。 Less convenient? 不太方便? Sure. 当然。 But "works" is better than "convenient". 但是“作品”比“便利”要好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.