【问题标题】:C# how to convert IEnumerable anonymous lists into data tableC#如何将IEnumerable匿名列表转换为数据表
【发布时间】:2012-10-31 07:39:59
【问题描述】:

有许多解决方案可以将列表转换为使用反射的DataTable,并且适用于转换匿名类型。但是,如果有 很多 个匿名类型列表,那么性能可能会成为问题。

这是从列表中创建DataTable 的唯一方法吗?有更快的方法吗?

【问题讨论】:

  • 在你使用 数千个匿名列表之前,我会切换到声明类型。
  • 完整性检查:为什么您会将匿名类型列表转换为DataTable?这似乎是一个下降...DataTable 通常不是一个理想的 API - 在大多数情况下,常规类更可取。命名类当然比匿名类型更可取,但就个人而言我会选择匿名类型而不是 DataTable 一周中的大部分时间。

标签: c# linq datatable ienumerable


【解决方案1】:

使用正确命名的 POCO/DTO/etc 类绝对会更好,但它仍然可以完成。反射的成本可以通过使用元编程来消除,最好是使用预卷库,例如FastMember,如下所示。

请注意,匿名类型的使用已强制在此处使用IList(而不是IList<T>List<T> 等)。使用命名类型最好使用通用版本。这将允许进行一些更改 - 特别是,itemType 将是 typeof(T),即使对于空表也可以创建正确的列。也许更重要的是,它将强制列表是同质的,而不是必须对此做出假设。

using FastMember;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
static class Program
{
    static void Main()
    {
        var list = GetList();
        var table = ToTable(list);
    }
    static DataTable ToTable(IList source)
    {
        if (source == null) throw new ArgumentNullException();
        var table = new DataTable();
        if (source.Count == 0) return table;

        // blatently assume the list is homogeneous
        Type itemType = source[0].GetType();
        table.TableName = itemType.Name;
        List<string> names = new List<string>();
        foreach (var prop in itemType.GetProperties())
        {
            if (prop.CanRead && prop.GetIndexParameters().Length == 0)
            {
                names.Add(prop.Name);
                table.Columns.Add(prop.Name, prop.PropertyType);
            }
        }
        names.TrimExcess();

        var accessor = TypeAccessor.Create(itemType);
        object[] values = new object[names.Count];
        foreach (var row in source)
        {
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = accessor[row, names[i]];
            }
            table.Rows.Add(values);
        }
        return table;
    }
    static IList GetList()
    {
        return new[] {
            new { foo = "abc", bar = 123},
            new { foo = "def", bar = 456},
            new { foo = "ghi", bar = 789},
        };
    }
}

【讨论】:

  • Mod to support nullable: // 添加了 Nullable 支持 (@crokusek) - stackoverflow.com/a/23233413/538763 - @Damith table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop .PropertyType) ?? prop.PropertyType);这被拒绝为直接编辑,说评论会更好。
  • 什么是 TypeAccessor
  • @ITGenius 它是FastMember 中的一个类(在文本中链接),通过元编程提供了对反射的优化访问级别
【解决方案2】:

改进了@MarcGravell's 回复支持:

  1. 一个可选的字段列表,指定要保留的列及其顺序。
  2. 可为空的类型。

    static public DataTable ToDataTable(this IList anonymousSource, List<string> keepOrderedFieldsOpt = null)
    {
        // https://stackoverflow.com/a/13153479/538763 - @MarcGravell
        // Added keepOrderedFieldsOpt, nullable types - @crokusek
    
        if (anonymousSource == null) throw new ArgumentNullException();
        DataTable table = new DataTable();
        if (anonymousSource.Count == 0) return table;
    
        // blatently assume the list is homogeneous
        Type itemType = anonymousSource[0].GetType();
        table.TableName = itemType.Name;            
    
        // Build up orderedColumns
        //
        List<PropertyInfo> orderedColumns;            
        if (keepOrderedFieldsOpt != null)
        {
            Dictionary<string, PropertyInfo> propertiesByName = itemType.GetProperties()
                .ToDictionary(p => p.Name, p => p);
    
            orderedColumns = new List<PropertyInfo>();
            List<string> missingFields = null;
    
            foreach (string field in keepOrderedFieldsOpt)
            {
                PropertyInfo tempPropertyInfo;
                if (propertiesByName.TryGetValue(field, out tempPropertyInfo))
                    orderedColumns.Add(tempPropertyInfo);
                else
                    (missingFields ?? (missingFields = new List<string>())).Add(field);
            }
    
            if (missingFields != null) 
                throw new ArgumentOutOfRangeException("keepOrderedFieldsOpt", "Argument keepOrderedFieldsOpt contains invalid field name(s): " + String.Join(", ", missingFields));
        }
        else
            orderedColumns = itemType.GetProperties().ToList();
    
        List<string> names = new List<string>();
        foreach (PropertyInfo prop in orderedColumns)
        {
            if (prop.CanRead && prop.GetIndexParameters().Length == 0)
            {
                names.Add(prop.Name);
    
                // Nullable support from stackoverflow.com/a/23233413/538763 - @Damith
                table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
            }
        }
        names.TrimExcess();
    
        TypeAccessor accessor = TypeAccessor.Create(itemType);
        object[] values = new object[names.Count];
        foreach (var row in anonymousSource)
        {
            for (int i = 0; i < values.Length; i++)
                values[i] = accessor[row, names[i]];
    
            table.Rows.Add(values);
        }
        return table;
    }
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多