0

我目前正在尝试借助 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%(根据分析器)

有人可以解释为什么会这样吗?

4

2 回答 2

3

如果没有符合您的条件的实体,您可以使用Max()获取最大位置而不是订购和获取第一项,并提供默认值(int 为零)。DefaultIfEmpty()顺便说一句,如果序列为空,您可以提供自定义默认值以返回。

lastPosition = someObjectContext.someObjectSet
                                .Where(i => i.PNT_ATT_ID == tmp_ATT_ID)
                                .Select(i => i.Position)
                                .Cast<int>()
                                .DefaultIfEmpty() 
                                .Max();

因此,您将避免执行两个查询 - 一个用于定义是否有任何位置,另一个用于获取最新位置。

于 2013-06-19T14:22:43.613 回答
3

我希望该更改会加快该方法的速度,因为我不确定编译器是否会注意到查询已完成两次并缓存结果。

它不会。事实上它不能。数据库可能不会为这两个查询返回相同的结果。在第一个查询之后和第二个查询之前添加或删除结果是完全可能的。(使此代码不仅效率低下,而且如果发生这种情况可能会损坏。)由于您完全有可能希望执行两个查询,知道结果可能不同,因此查询的结果被重新定义是很重要的。用过的。

这里的重点是延迟执行的想法。 relevantEntities 不是查询的结果,而是查询本身。直到IQueryable被迭代(通过诸如CountFirstforeach循环等方法)才会查询数据库,并且每次迭代查询时,它都会对数据库执行另一个查询。

在您的情况下,您可以这样做:

var lastPosition = someObjectContext.someObjectSet
    .Where(i => i.PNT_ATT_ID == tmp_ATT_ID)
    .OrderByDescending(i => i.Position)
    .Select(i => i.Position)
    .Cast<int>()
    .FirstOrDefault();

这利用了 an 的默认值为 0 的事实int,这是您在之前不匹配的情况下设置的值。

请注意,这是一个与您的功能相同的查询,它只是避免执行两次。一个更好的查询将是lazyberezovsky建议的查询,您可以在其中利用Max而不是订购和获取第一个查询。如果该列上有索引,则不会有太大区别,但是如果没有索引,则排序会贵得多。

于 2013-06-19T15:00:11.683 回答