为什么 Equals 被调用了 50M 次?
听起来很可疑。您有 10,000 条评论和 50,000,000 通电话Equals
。假设这是由 EF 内部实现的恒等映射引起的。身份映射确保具有唯一键的每个实体仅被上下文跟踪一次,因此如果上下文已经具有与从数据库加载的记录具有相同键的实例,则它将不会具体化新实例,而是使用现有实例。现在这怎么能与这些数字相符呢?我的可怕猜测是:
=============================================
1st record read | 0 comparisons
2nd record read | 1 comparison
3rd record read | 2 comparisons
...
10.000th record read | 9.999 comparisons
这意味着每个新记录都会与身份映射中的每个现有记录进行比较。通过应用数学来计算所有比较的总和,我们可以使用称为“算术序列”的东西:
a(n) = a(n-1) + 1
Sum(n) = (n / 2) * (a(1) + a(n))
Sum(10.000) = 5.000 * (0 + 9.999) => 5.000 * 10.000 = 50.000.000
我希望我的假设或计算没有错误。等待!我希望我做错了,因为这看起来不太好。
尝试关闭更改跟踪 = 希望关闭身份映射检查。
这可能很棘手。从...开始:
var bookAndReviews = db.Books.Where(b => b.BookId == id)
.Include(b => b.Reviews)
.AsNoTracking()
.FirstOrDefault();
但是您的导航属性很有可能不会被填充(因为它是通过更改跟踪处理的)。在这种情况下使用这种方法:
var book = db.Books.Where(b => b.BookId == id).AsNoTracking().FirstOrDefault();
book.Reviews = db.Reviews.Where(r => r.BookId == id).AsNoTracking().ToList();
不管怎样,你能看到传递给 Equals 的对象类型是什么吗?我认为它应该只比较主键,甚至 50M 整数比较也不应该是这样的问题。
顺便说一句,EF 很慢 - 这是众所周知的事实。在具体化实体时,它还在内部使用反射,因此仅仅 10,000 条记录就可能需要“一些时间”。除非您已经这样做了,否则您还可以关闭动态代理创建(db.Configuration.ProxyCreationEnabled
).