IEnumerable为DataTable性能问题

时间:2011-08-17 13:08:23

标签: c# linq performance datatable ienumerable

我有以下扩展程序,它会从DataTable生成IEnumerable

    public static DataTable AsDataTable<T>(this IEnumerable<T> enumerable)
    {
        DataTable table = new DataTable();

        T first = enumerable.FirstOrDefault();
        if (first == null)
            return table;

        PropertyInfo[] properties = first.GetType().GetProperties();
        foreach (PropertyInfo pi in properties)
            table.Columns.Add(pi.Name, pi.PropertyType);

        foreach (T t in enumerable)
        {
            DataRow row = table.NewRow();
            foreach (PropertyInfo pi in properties)
                row[pi.Name] = t.GetType().InvokeMember(pi.Name, BindingFlags.GetProperty, null, t, null);
            table.Rows.Add(row);
        }

        return table;
    }

然而,在大量数据上,性能不是很好。是否有任何明显的性能修复我无法看到?

4 个答案:

答案 0 :(得分:2)

您可以始终使用像Fasterflect这样的库来发出IL,而不是对列表中每个项目的每个属性使用true Reflection。不确定与DataTable的任何问题。

或者,如果此代码不是试图成为通用解决方案,那么您可以始终将IEnumerable内的任何类型转换为DataRow,从而避免一起反映。

答案 1 :(得分:2)

首先,一些非性能问题:

  1. 枚举中第一个项的类型可能是T的子类,它定义了可能不存在于其他项上的属性。为避免可能导致的问题,请使用T类型作为属性列表的源。
  2. 该类型可能具有无getter或具有索引getter的属性。您的代码不应尝试读取其值。
  3. 就事情而言,我可以看到反射和数据表加载方面的潜在改进:

    1. 缓存属性getter并直接调用它们。
    2. 避免按名称访问数据行列以设置行值。
    3. 在添加行时将数据表置于“数据加载”模式。
    4. 使用这些mod,您最终会得到以下内容:

      public static DataTable AsDataTable<T>(this IEnumerable<T> enumerable)
      {
          if (enumerable == null)
          {
              throw new ArgumentNullException("enumerable");
          }
      
          DataTable table = new DataTable();
          if (enumerable.Any())
          {
              IList<PropertyInfo> properties = typeof(T)
                                                  .GetProperties()
                                                  .Where(p => p.CanRead && (p.GetIndexParameters().Length == 0))
                                                  .ToList();
      
              foreach (PropertyInfo property in properties)
              {
                  table.Columns.Add(property.Name, property.PropertyType);
              }
      
              IList<MethodInfo> getters = properties.Select(p => p.GetGetMethod()).ToList();
      
              table.BeginLoadData();
              try
              {
                  object[] values = new object[properties.Count];
                  foreach (T item in enumerable)
                  {
                      for (int i = 0; i < getters.Count; i++)
                      {
                          values[i] = getters[i].Invoke(item, BindingFlags.Default, null, null, CultureInfo.InvariantCulture);
                      }
      
                      table.Rows.Add(values);
                  }
              }
              finally
              {
                  table.EndLoadData();
              }
          }
      
          return table;
      }
      

答案 2 :(得分:1)

而不是:

row[pi.Name] = t.GetType().InvokeMember(pi.Name, BindingFlags.GetProperty, null, t, null);

使用:

row[pi.Name] = pi.GetValue(t, null);

答案 3 :(得分:1)

您可能没有选择,但可能会查看代码的体系结构,看看您是否可以避免使用DataTable而是自己返回IEnumerable<T>

这样做的主要原因是:

  1. 您将从IEnumerable转到DataTable,它实际上是从流式操作转移到缓冲操作。

    • 流式传输:使用yield return,以便仅在需要时将结果从枚举中拉出。它不像foreach

    • 那样一次迭代整个集合
    • 缓冲:将所有结果提取到内存中(例如填充的集合,数据表或数组),这样就可以立即产生所有费用。

  2. 如果您可以使用IEnumerable返回类型,那么您可以自己使用yield return关键字,这意味着您可以将所有反射的成本分摊,而不是一次性产生所有这些。