2014-11-04 191 views
2

我使用ASP NET MVC 4.5和EF6,代码优先迁移。EF LINQ ToList非常慢

我有这个代码,大约需要6秒。

var filtered = _repository.Requests.Where(r => some conditions); // this is fast, conditions match only 8 items 
var list = filtered.ToList(); // this takes 6 seconds, has 8 items inside 

我认为这是因为关系的,它必须建立他们的内部内存,但事实并非如此,因为甚至当我返回0领域,它仍然是慢

var filtered = _repository.Requests.Where(r => some conditions).Select(e => new {}); // this is fast, conditions match only 8 items 
var list = filtered.ToList(); // this takes still around 5-6 seconds, has 8 items inside 

现在请求表是相当复杂的,大量的关系,并拥有16K〜项目。另一方面,过滤的列表应该只包含8个项目的代理。

为什么ToList()方法这么慢?我实际上认为问题不在于ToList()方法,但可能是EF问题,或者设计问题不好。

任何人都有过这样的经验吗?

编辑:

这些条件:

_repository.Requests.Where(r => ids.Any(a => a == r.Student.Id) && r.StartDate <= cycle.EndDate && r.EndDate >= cycle.StartDate) 

所以基本上,我可以,如果Student ID是我的ID列表检查和检查,如果日期匹配。

+0

你是否在'some condition'中使用了任何导航属性,因为它导致了sql方面的连接? – dotctor 2014-11-04 11:00:22

+0

作为@HamidP的附加,是否将您的导航属性标记为“虚拟”?否则,他们会立即加入,而不是延迟加载(纠正我,如果我错了。) – Marthijn 2014-11-04 13:18:29

+0

是的,他们都是虚拟的。 – Jaanus 2014-11-04 13:23:00

回答

3

除了马腾的答案,我认为这个问题是关于两个不同的情况

  1. some condition是复杂的,结果在复杂庞大的在你的数据库连接或查询

  2. some condition是在过滤列没有索引,这会导致全表扫描并使查询变慢。

我建议开始监测由实体框架生成查询,这很简单,你只需要设置上下文Log功能和看到的结果,

using (var context = new MyContext()) 
{ 
    context.Database.Log = Console.Write; 

    // Your code here... 
} 

如果你看到一些奇怪的生成的查询尝试通过将其部分分解来使其更好,有时候Entity Framework生成的查询不太好。

如果查询没问题,那么问题在于你的数据库(假设没有网络问题)。

使用SQL事件探查器运行您的查询并检查出现了什么问题。

UPDATE

我建议你:在你的餐桌

  1. 附加指数StartDateEndDate列(每一个,不是一个两个)
+0

我在评论中添加了条件。 – Jaanus 2014-11-04 11:54:59

+0

什么是IDS?整数列表?它的大小是多少? – dotctor 2014-11-04 11:57:42

+0

它可以从1到100左右不等,但即使有1个ID也很慢。它是学生ID上的整数列表。 – Jaanus 2014-11-04 12:00:47

2

ToList对数据库执行查询,而第一行不是。

你能在这里显示some conditions的代码? 要提高性能,您需要优化数据库表上的查询/创建索引。

+0

我在评论中添加了条件。 – Jaanus 2014-11-04 11:53:41

+0

@Jaanus你能告诉你如何获得'ids'? – 2014-11-04 15:18:00

5

filtered变量包含查询这是一个问题,它不包含答案。如果您通过调用.ToList()来请求答案,即执行查询时。这就是为什么它很慢的原因,因为只有当您拨打.ToList()时,才会执行您的数据库查询。

它被称为延期执行A google可能会给你一些关于它的更多信息。

如果您显示了一些您的情况,我们可能会说出为什么它很慢。

+0

我在评论中添加了条件。 – Jaanus 2014-11-04 11:53:56

+0

如果你将'ids.Any(a => a == r.Student.Id)''改为'ids.Contains(r.Student.Id)'',你能检查它是否有效吗?这仍然意味着你需要额外的表格。 'Student.Id'是关键还是索引? – Maarten 2014-11-04 12:05:49

+0

不是,它实际上是之前包含的,更改为任何因为我认为它会使其更快。学生ID是整数,主键 – Jaanus 2014-11-04 12:29:17

0

Jaanus。此问题最可能的原因是实体框架生成的SQL查询的可扩展性。我想你的过滤条件包含一些其他表的检查。

尝试检查“SQL Server Profiler”生成的查询。然后将此查询复制到“Management Studio”并检查“预计执行计划”。作为一项规则,“管理工作室”为您的查询生成索引建议尝试遵循这些建议。

1

你的第一个代码行仅返回IQueryable。这是您希望运行的查询的表示,而不是查询的结果。当您在IQueryable上调用.ToList()时,查询本身仅在数据库上运行,因为它实际上是第一个要求输入数据的点。

您的调整添加.Select只增加了现有的IQueryable查询定义。它不会改变要执行的条件。你已经根本改变了以下内容,其中你回来8条:

select * from Requests where [some conditions]; 

喜欢的东西:

select '' from Requests where [some conditions]; 

你仍然必须执行与给你8条条件的完整的查询,但对于每一个,你只要求一个空字符串,所以你回来8个空字符串。

长而短的一点是,您遇到的任何性能问题都来自您的“某些条件”。没有看到它们,它很难知道。但是我曾经看到过去人们在调用.ToList()之前在循环内部添加了子句,并且无意中创建了大量复杂的查询。

+0

我在评论中添加了条件。 – Jaanus 2014-11-04 11:58:59

+0

什么类型是IDS?如果它是另一个IQueriable或List?如果它是一个IQueryable,你将重新评估它的请求表 – Keith 2014-11-04 13:35:14

+0

的每个记录你也可以尝试先运行日期比较,因为它会很快,然后过滤是通过ID:_repository.Requests.Where(r => r.StartDate <= cycle.EndDate && r.EndDate> = cycle.StartDate).where(r => ids.Contains(r.Student.Id)); – Keith 2014-11-04 13:36:47