我目前正在尝试借助VS-Profiling工具优化.net应用程序。
一个经常调用的函数包含以下代码:
if (someObjectContext.someObjectSet.Where(i => i.PNT_ATT_ID == tmp_ATT_ID).OrderByDescending(i => i.Position).Select(i => i.Position).Count() == 0)
{
lastPosition = 0;
}
else
{
lastPosition = someObjectContext.someObjectSet.Where(i => i.PNT_ATT_ID == tmp_ATT_ID).OrderByDescending(i => i.Position).Select(i => i.Position).Cast<int>().First();
}
我改变了这样的事情:
var relevantEntities = someObjectContext.someObjectSet.Where(i => i.PNT_ATT_ID == tmp_ATT_ID).OrderByDescending(i => i.Position).Select(i => i.Position);
if (relevantEntities.Count() == 0)
{
lastPosition = 0;
}
else
{
lastPosition = relevantEntities.Cast<int>().First();
}
我希望这个改变会加快方法的速度,因为我不确定编译器是否会注意到查询已完成两次并缓存结果。
令我惊讶的是,该方法的执行时间(墨水样本的数量)没有减少,但甚至增加了9%(根据剖析器)
有人可以解释为什么会这样吗?
答案 0 :(得分:3)
如果没有符合条件的实体,您可以使用Max()
获取最大位置而不是排序和获取第一项,并DefaultIfEmpty()
提供默认值(int为零)。顺便说一句,如果序列为空,你可以提供自定义默认值。
lastPosition = someObjectContext.someObjectSet
.Where(i => i.PNT_ATT_ID == tmp_ATT_ID)
.Select(i => i.Position)
.Cast<int>()
.DefaultIfEmpty()
.Max();
因此,您将避免执行两个查询 - 一个用于定义是否有任何位置,另一个用于获取最新位置。
答案 1 :(得分:3)
我希望更改会加快方法的速度,因为我不确定编译器是否会注意到查询已完成两次并缓存结果。
不会。事实上它不能。数据库可能不会为两个查询返回相同的结果。在第一次查询之后和第二次查询之前添加或删除结果是完全可能的。 (使这些代码不仅低效,但如果发生这种情况可能会破坏。)因为你完全有可能想要执行两个查询,因为知道结果会有所不同,所以结果很重要 not 的查询可以重复使用。
这里重点是延迟执行的想法。 relevantEntities
不是查询的结果,而是查询本身。直到IQueryable
被迭代(通过诸如Count
,First
,foreach
循环等方法),才会查询数据库,并且每次都要查询你迭代查询它将对数据库执行另一个查询。
在你的情况下,你可以这样做:
var lastPosition = someObjectContext.someObjectSet
.Where(i => i.PNT_ATT_ID == tmp_ATT_ID)
.OrderByDescending(i => i.Position)
.Select(i => i.Position)
.Cast<int>()
.FirstOrDefault();
这利用了int
的默认值为0的事实,这是您在之前不匹配的情况下将值设置为的内容。
请注意,这是一个功能与您相同的查询,它只是避免执行两次。更好的查询是由lazyberezovsky建议的,您利用Max
而不是排序并获取第一个查询。如果该列上有索引就没有太大差别,但如果不,索引排序会更加昂贵。