我们有一个使用代码优先方法的Web MVC .NET应用程序。执行基本CRUD操作时,一切都正常,但我们的报告和长时间运行的任务将永远完成。
我们的大多数有问题的案例都可以通过以下两种情况恢复:
sqlDataReader
),制作一些
更改并为每个人创建一个副本。sqlDataReader
)来读取一些信息并写入文件。在意识到处理我们的任务和报告所需的时间呈指数级增长(O(n^2)
)与要处理的项目数量之后,我们进一步调查并找出问题的根源:实体框架中的变更跟踪。
如果我们停用AutoDetectChangesEnabled
,一切正常,但我们在该主题上阅读的每篇文章都说我们应该尽可能避免这样做。
所以这是我的问题:是否禁用AutoDetectChangesEnabled
解决这类问题的唯一或推荐方法?还有其他清洁解决方案吗?你会怎么做到这一点?
这假设我们将遵循本系列文章中所述的所有建议:https://blog.oneunicorn.com/2012/03/10/secrets-of-detectchanges-part-1-what-does-detectchanges-do/
这是一个小代码示例,说明了方案2:
//SQL query to fetch 100,000+ items
SqlCommand command = new SqlCommand(query, conn);
//Use SqlDataReader to stream results
using (SqlDataReader reader = command.ExecuteReader())
{
/*In the following loop, if we use SqlCommand instead of LINQ, it's fast and steady.
But as soon as we start using LINQ, each iteration takes exponentially more time
to complete - even if we are only making read operations. Unless we disable
AutoDetectChangesEnabled: then it's fast and steady.*/
while (reader.Read())
{
/*Fetch additional information. I know this could be done with a join in the
original query but we have a lot of additional info to fetch and the query would
be too heavy (this is a simplified version of the code).*/
Item item = db.Items.Where(x => x.OtherItem_Id == (int)reader["Id"]).FirstOrDefault();
//Read properties
prop1 = item.prop1;
prop2 = item.prop2;
prop3 = item.prop3;
//Use those properties to add an entry to a csv file
}
}
谢谢!
答案 0 :(得分:1)
如果没有具体的例子,目前很难回答你的问题。正如Brian在评论部分所说,他们可以拥有无数可能的解决方案......
例如,一个解决方案可以像使用AddRange一样简单,而不是每次在DataReader中循环时添加实体。
var listA = new List<A>();
while(dr.Read())
{
var itemA = //...code...
// ctx.Add(itemA); // DON'T do it...
list.Add(itemA);
}
ctx.AddRange(listA);
DetectChanges方法只会调用一次而不是X次。
免责声明:我是该项目的所有者Entity Framework Extensions
另一个解决方案可能是我最近收到的同样问题。由于拥有一个非常复杂和大型的模型,检测变更对于某人来说需要1个多小时。
我们只是建议他直接从我们的库中使用BulkInsert插入:Entity Framework Extensions,它将性能提高了50倍以上,而不是使用SaveChanges。
编辑:回答子问题
启用AutoDetectChanges时,即使读取操作也会减慢速度
像find这样的读取方法会自动调用DetectChanges,所以是的,一些读操作也会受到DetectChanges的影响
public TEntity Find(params object[] keyValues)
{
this.InternalContext.ObjectContext.AsyncMonitor.EnsureNotEntered();
this.InternalContext.DetectChanges(false);
// ...code
}
答案 1 :(得分:1)
可能有多种方法可以提高性能,但正如上面所述,它很难帮助解决这类问题,因为代码的其他部分可能影响到我们无法看到。话虽如此,这里有一些想法。
1 - 您的EF LINQ查询位于100,000个项目的自描述循环中。这意味着您将在此循环期间向数据库发出100,000个查询。根据{{1}}表大小和复杂性,您可以将所有这些记录放入内存中,然后在内存列表中执行LINQ命令。这会使你的SQL查询减少99,999。
Items
2 - 您可以直接在查询中触发using (SqlDataReader reader = command.ExecuteReader())
{
//pull the entire table to an in-memory list.
var items = db.Items.ToList();
while (reader.Read())
{
Item item = items.Where(x => x.OtherItem_Id == (int)reader["Id"]).FirstOrDefault();
prop1 = item.prop1;
prop2 = item.prop2;
prop3 = item.prop3;
}
}
。如果这是真正的只读数据集,那么你就不会有任何危险,因为你没有打电话给NoTracking
db.SaveChanges()