0

我有一种情况,循环遍历 LINQ 的结果让我很紧张。那么这是我的场景:

我有一个来自数据库的 DataTable,我从中获取数据:

var results = from d in dtAllData.AsEnumerable()
              select new MyType
              {
                  ID = d.Field<Decimal>("ID"),
                  Name = d.Field<string>("Name")
              }

根据排序顺序进行排序后:

if(orderBy != "")
{
string[] ord = orderBy.Split(' ');
if (ord != null && ord.Length == 2 && ord[0] != "")
                        {
                            if (ord[1].ToLower() != "desc")
                            {
                                results = from sorted in results
                                          orderby GetPropertyValue(sorted, ord[0])
                                          select sorted;
                            }
                            else
                            {
                                results = from sorted in results
                                          orderby GetPropertyValue(sorted, ord[0]) descending
                                          select sorted;
                            }
                        }
}

GetPropertyValue 方法如下:

private object GetPropertyValue(object obj, string property)
    {
        System.Reflection.PropertyInfo propertyInfo = obj.GetType().GetProperty(property);
        return propertyInfo.GetValue(obj, null);
    }

在此之后,我为第一页取出 25 条记录,例如:

results = from sorted in results
          .Skip(0)
          .Take(25)
          select sorted;

到目前为止一切进展顺利,现在我必须将此结果传递给一个方法,该方法将对数据进行一些操作并返回给我所需的数据,在这个方法中,当我想循环这 25 条记录时,它会很好足够的时间。我的方法定义是:

public MyTypeCollection GetMyTypes(IEnumerable<MyType> myData, String dateFormat, String offset)

我已经尝试过foreach了,在我的机器上大约需要 8-10 秒,这条线需要时间:

foreach(var _data in myData)

我尝试了while循环并且正在做同样的事情,我像这样使用它:

var enumerator = myData.GetEnumerator();
while(enumerator.MoveNext())
{
    int n = enumerator.Current;
   Console.WriteLine(n);
}

这段代码需要时间MoveNext

比我去的for循环像:

int length = myData.Count();
for (int i = 0; i < 25;i++ )
{
     var temp = myData.ElementAt(i);
}

此代码需要时间ElementAt

谁能指导我,我做错了什么。我在 VS 2008 中使用框架 3.5。

提前致谢

4

3 回答 3

3

编辑:我怀疑问题出在您的订购方式上。您正在使用反射首先获取然后为每个记录调用一个属性。即使您只想要前 25 条记录,它也必须首先调用所有记录才能对它们进行排序GetPropertyValue

如果你可以在没有反射的情况下做到这一点会更好......但如果你确实需要使用反射,至少调用Type.GetProperty() 一次而不是每条记录。


(在某些方面,这更多是为了帮助您更轻松地诊断问题,而不是完整的答案......)

正如 Henk 所说,这很奇怪:

results = from sorted in results
          .Skip(0)
          .Take(25)
          select sorted;

你几乎可以肯定真的只是想要:

results = results.Take(25);

Skip(0)毫无意义。)

它实际上可能没有帮助,但它会使代码更易于调试。

下一个问题是我们实际上无法看到您的所有代码。你写过:

根据排序顺序进行排序后

...但你还没有展示你是如何执行订购的

您应该向我们展示一个从DataTable使用到使用的完整示例。

改变你对序列的迭代方式将无济于事——无论哪种方式,它都会做同样的事情,真的——尽管令人惊讶的是,在你最后一次尝试中,它Count()显然工作得很快。坚持foreach- 但要弄清楚这将要做什么。LINQ 使用了很多惰性求值,如果你做了一些使事情变得非常繁重的事情,那可能就是问题所在。没有看到整个管道很难知道。

于 2012-06-28T15:40:28.923 回答
0

问题是您的“结果” IEnumerable 实际上并没有被评估,直到它被传递到您的方法并被枚举。这意味着整个操作,从 dtAllData 获取所有数据,选择新类型(这发生在整个可枚举上,而不仅仅是前 25 个),最后是 take 25 操作,都发生在第一个枚举上的 IEnumerable (foreach,while,whatever)。

这就是为什么你的方法需要这么长时间。它实际上是在做一些在方法内部其他地方定义的工作。如果您希望在您的方法之前发生这种情况,您可以在方法之前执行“ToList()”。

于 2012-06-28T15:38:17.460 回答
0

您可能会发现采用混合方法更容易;

为了:

1)就地对数据表进行排序。最好在数据库级别执行此操作,但是,如果不能,则 DataTable.DefaultView.Sort 非常有效:

dtAllData.DefaultView.Sort = ord[0] + " " + ord[1];

这假设 ord[0] 是列名,而 ord[1] 是 ASC 或 DESC

2)按索引浏览DefaultView:

int pageStart = 0;

List<DataRowView> pageRows = new List<DataRowView>();

for (int i = pageStart; i < dtAllData.DefaultView.Count; i++ )
{
   if(pageStart + 25 > i || i == dtAllData.DefaultView.Count - 1) { break; //Exit if more than the number of pages or at the end of the rows }
   pageRows.Add(dtAllData.DefaultView[i]);
}

...并从这个小得多的列表中创建您的对象...(我假设这些列称为 Id 和 Name,以及类型)

List<MyType> myObjects = new List<MyType>();

foreach(DataRowView pageRow in pageRows)
{
  myObjects.Add(new MyObject() { Id = Convert.ToInt32(pageRow["Id"]), Name = Convert.ToString(pageRow["Name"])});
}

然后,您可以继续您正在做的其他事情。

于 2012-06-28T16:19:43.137 回答