如何在C#中将IEnumerable<T>列表转换为Datatable

3
我卡在了以下问题上:
我通常使用下面的函数将自定义数据传输到DataTable,但在这种情况下,输入数据是由列表组成的类。我只需要在每行中写入每个列表的第一个元素。你能帮我调整这个函数以实现这个目标吗?
public List<int> xID { get; set; }
public List<string> xName { get; set; }
public List<string> xType { get; set; }
public List<string> xSource { get; set; }
public List<int> xmdID { get; set; }
public List<string> xMDName { get; set; }
public List<string> xUser { get; set; }

public static DataTable ListToDataTable<T>(IEnumerable<T> list)
{
    Type type = typeof(T);
    var properties = type.GetProperties();

    DataTable dataTable = new DataTable();
    foreach (PropertyInfo info in properties)
    {
        dataTable.Columns.Add(new DataColumn(info.Name, info.PropertyType));
    }

    foreach (T entity in list)
    {
        object[] values = new object[properties.Length];
        for (int i = 0; i < properties.Length; i++)
        {
            values[i] = properties[i].GetValue(entity);
        }

        dataTable.Rows.Add(values);
    }

    return dataTable;
}

基于Carra的回答,我尝试了下面的代码,但它没有识别出pType类型(类型或命名空间名称“pType”无法找到(是否缺少using指令或程序集引用?))
var v = properties[i].GetValue(entity);
Type pType = properties[i].PropertyType;

if (pType.IsGenericType && pType.GetGenericTypeDefinition() == typeof(Nullable<>))
pType = Nullable.GetUnderlyingType(pType);

                if (pType.IsEnum)
                    pType = Enum.GetUnderlyingType(pType);

                if (v.GetType().GetGenericTypeDefinition() == typeof(List<>))
                {
                    values[i] = (v as List<pType>).First();
                }

更新

我想这不是最好的解决方案,因为它不接受任何列表类型,但这是我能做到的最好的:

    public static DataTable ListToDataTable<T>(IEnumerable<T> list)
    {
        Type type = typeof(T);
        var properties = type.GetProperties();

        DataTable dataTable = new DataTable();
        foreach (PropertyInfo info in properties)
        {
            Type propertyType = info.PropertyType;

            if (propertyType.IsGenericType | propertyType.GetGenericTypeDefinition() == typeof(Nullable<>) | propertyType.IsEnum)
                propertyType = propertyType.GetGenericArguments()[0];

            dataTable.Columns.Add(new DataColumn(info.Name, propertyType));
        }

        foreach (T entity in list)
        {
            object[] values = new object[properties.Length];
            for (int i = 0; i < properties.Length; i++)
            {
                var v = properties[i].GetValue(entity);
                Type pType = v.GetType().GetGenericArguments().FirstOrDefault();

                if (v.GetType().GetGenericTypeDefinition() == typeof(List<>))
                {
                    if(pType == typeof(int))
                        values[i] = (v as List<int>).First();
                    else if(pType == typeof(string))
                        values[i] = (v as List<string>).First();
                }
                else
                {
                    values[i] = v;
                }
            }
            dataTable.Rows.Add(values);
        }
        return dataTable;
    }

2
如果您已经编写了这个,那么至少尝试一下也不难,对吧? - nvoigt
2个回答

1
你需要类似这样的东西:
foreach (PropertyInfo info in properties)
{
    if(info.PropertyType == typeof(IEnumerable))
    {
       dataTable.Columns.Add(new DataColumn(info.Name, info.Cast<object>().First().GetType());
    }
    else
    {
       dataTable.Columns.Add(new DataColumn(info.Name, info.PropertyType));
    }
}

foreach (T entity in list)
{
    object[] values = new object[properties.Length];
    for (int i = 0; i < properties.Length; i++)
    {
        var v = properties[i].GetValue(entity);
        if(v is IEnumerable)
        {
          values[i] = (v.Cast<object>().First()).First();
        }
        else
        {
          values[i] = v;
        }
    }

    dataTable.Rows.Add(values);
}

即使我添加了“using System.Linq”,编译器仍然无法识别“.First()”。 'System.Collections.IEnumerable'不包含“First”的定义,也找不到接受类型为'System.Collections.IEnumerable'的第一个参数的扩展方法“First”(您是否缺少使用指令或程序集引用?) - Gabriel
IEnumerable 不支持 Linq,你可以将其转换为 IEnumerable<object> 来使用 First 方法。 - Carra

0

这对我有用。我在这里找到了它并稍微修改了一下。

public static DataTable ToDataTable<T>(this System.Collections.Generic.List<T> collection, string _sTableName) {
        var table = new DataTable(_sTableName);
        var type = typeof(T);
        var properties = type.GetProperties();

        //Create the columns in the DataTable
        foreach (var property in properties) {
            if (property.PropertyType == typeof(int?)) {
                table.Columns.Add(property.Name, typeof(int));
            } else if (property.PropertyType == typeof(decimal?)) {
                table.Columns.Add(property.Name, typeof(decimal));
            } else if (property.PropertyType == typeof(double?)) {
                table.Columns.Add(property.Name, typeof(double));
            } else if (property.PropertyType == typeof(DateTime?)) {
                table.Columns.Add(property.Name, typeof(DateTime));
            } else if (property.PropertyType == typeof(Guid?)) {
                table.Columns.Add(property.Name, typeof(Guid));
            } else if (property.PropertyType == typeof(bool?)) {
                table.Columns.Add(property.Name, typeof(bool));
            } else {
                table.Columns.Add(property.Name, property.PropertyType);
            }
        }

        //Populate the table
        foreach (var item in collection) {
            var row = table.NewRow();
            row.BeginEdit();
            foreach (var property in properties) {
                row[property.Name] = property.GetValue(item, null) ?? DBNull.Value;
            }

            row.EndEdit();
            table.Rows.Add(row);
        }

        return table;
    }

这样的类型检查并不好。就像你链接的例子一样,应该使用 Nullable.GetUnderlyingType 方法来处理这些通用情况。 - thehennyy
谢谢。实际上它没有获取每个列表的第一个元素。结果与我发布的代码相同。 - Gabriel
这段代码百分之百有效,已经在生产环境中运行了几年。如果您无法获取第一个项目,则问题必须在其他地方。 - Marko Juvančič
它获取了第一个项目,但同时也获取了所有其他项目,而我只需要第一个。 - Gabriel
好的,你只需要调用第一个项目: list.RemoveRange(1, list.Count - 1); dt = list.ToDataTable(); - Marko Juvančič

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接