枚举性能

时间:2014-01-23 07:42:15

标签: c# performance enumeration

请考虑以下代码示例:

IEnumerable<Y> myEnumeration = *some linq stuff into a datatable*

If (myEnumeration.Count() > X)
{
    foreach(Y bla in myEnumeration)
    {
       // do something
    }
 }

这会导致枚举2次吗?伯爵 - 召唤+ foreach?如果是这样,有没有办法避免其中一个枚举?

提前致谢

已编辑myEnumeration.Count - &gt; myEnumeration.Count()(扩展方法)

4 个答案:

答案 0 :(得分:3)

我把这段代码放到LinqPad让我展示生成的SQL:

IEnumerable<MyTable> myEnumeration = MyTable;

if (myEnumeration.Count() > 1)
{
    foreach(MyTable bla in myEnumeration)
    {
       // do something
    }
 }

生成的SQL如下:

SELECT * FROM [MyTable] AS [t0]
GO

SELECT *
FROM [MyTable] AS [t0]

所以是的,数据将从数据库中检索两次。 考虑

List<Y> myEnumeration = *some linq stuff into a datatable* **.ToList();**

答案 1 :(得分:3)

是的,这将为您提供两个数据库调用。 Count()将执行以下查询:

 SELECT COUNT(1) FROM Table WHERE Blah

然后GetEnumerator()将执行获取所有必填字段的查询:

 SELECT Id, Foo, Bar FROM Table WHERE Blah

实际上没有一个正确的答案。你应该考虑:

  • 您经常获得的结果数量(是数百万个实体,还是几十个实体)
  • 所需实体的数量(是几个实体还是数百个实体)
  • 更常见的是 - 结果集中是否存在所需实体的数量
  • 是真正的性能问题,或者此方法将每周调用一次

根据你的不同,你应该做出决定。

  • 如果不是性能问题,那么只需进行两次数据库调用
  • 如果退回的商品数量不是很大,而且他们更有可能包含所需数量的商品,那么只需将查询转储到列表
  • 如果项目数量很大并且您不想全部转储它们,那么您可以使用下面的扩展方法,检查结果集中是否至少有N个项目而不保存所有序列到列表。但在这里你应该考虑什么会更快 - 转储N个必需的项目,或者调用数据库来检查项目数。

这是扩展名:

public static IEnumerable<T> TakeIfMoreThan<T>(
    this IEnumerable<T> source, int count)
{
    List<T> buffer = new List<T>(count);

    using (var iterator = source.GetEnumerator())
    {
         while (buffer.Count < count && iterator.MoveNext())
                buffer.Add(iterator.Current);

         if (buffer.Count < count)
         {
              yield break;
         }
         else
         {
            foreach (var item in buffer)
                yield return item;

            buffer.Clear();
            while (iterator.MoveNext())
                yield return iterator.Current;
        }            
    }
}

用法很简单:

foreach(Y bla in myEnumeration.TakeIfMoreThan(X))
{
   // do something
}

因此,您不需要将所有查询结果转储到内存列表中。您将使用单个数据库调用(但它将查询所有项目字段)。如果结果数量少于所需数量,则不会枚举项目。

答案 2 :(得分:2)

如果Count不是属性,而是方法Enumerable.Count(如我所料)。在这种情况下,我建议的是事先将其列入一个列表:

List<Y> myList = *some linq stuff into a datatable*.ToList();
if (myList.Count > X)
    foreach (Y bla in myList)
        //do something

答案 3 :(得分:1)

它将为您生成两个查询:

SELECT COUNT(*) FROM sometable

SELECT * FROM sometable

既不是一次也不是两次。这将是两个单独的请求,但只有第二个请求将提取数据。第一个将返回数字,并将更快。如果您希望在严格的一个请求中执行此操作,请在查询结尾处使用.ToList()。然后它将通过列表。您也可以在SQL事件探查器中进行检查。请记住,在某些情况下,两种查询方法可能更安全,因此您不会突然开始拉出数百万行。