当前位置:  开发笔记 > 编程语言 > 正文

将通用List/Enumerable转换为DataTable?

如何解决《将通用List/Enumerable转换为DataTable?》经验,为你挑选了11个好方法。

我有几个返回不同通用列表的方法.

在.net中存在任何类静态方法或将任何列表转换为数据表的任何东西?我唯一可以想象的是使用Reflection来做到这一点.

如果我有这个:

List whatever = new List();

(下一个代码当然不起作用,但我想有可能:

DataTable dt = (DataTable) whatever;

Marc Gravell.. 310

这是使用NuGet的FastMember进行的 2013年更新:

IEnumerable data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data)) {
    table.Load(reader);
}

这使用FastMember的元编程API来获得最佳性能.如果您想将其限制为特定成员(或强制执行订单),那么您也可以这样做:

IEnumerable data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data, "Id", "Name", "Description")) {
    table.Load(reader);
}

编辑的Dis/claimer : FastMember是一个Marc Gravell项目.它的黄金和全飞!


是的,这几乎是完全相反的这一个; 反射就足够了 - 或者如果你需要更快,HyperDescriptor在2.0,或者Expression在3.5.实际上,HyperDescriptor应该绰绰有余.

例如:

// remove "this" if not on C# 3.0 / .NET 3.5
public static DataTable ToDataTable(this IList data)
{
    PropertyDescriptorCollection props =
        TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    for(int i = 0 ; i < props.Count ; i++)
    {
        PropertyDescriptor prop = props[i];
        table.Columns.Add(prop.Name, prop.PropertyType);
    }
    object[] values = new object[props.Count];
    foreach (T item in data)
    {
        for (int i = 0; i < values.Length; i++)
        {
            values[i] = props[i].GetValue(item);
        }
        table.Rows.Add(values);
    }
    return table;        
}

现在使用一行,您可以比反射快许多倍(通过启用HyperDescriptor对象类型T).


编辑重新性能查询; 这是一个测试台,结果如下:

Vanilla 27179
Hyper   6997

我怀疑瓶颈已从成员访问转移到DataTable性能......我怀疑你会在那方面有所改进......

码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
public class MyData
{
    public int A { get; set; }
    public string B { get; set; }
    public DateTime C { get; set; }
    public decimal D { get; set; }
    public string E { get; set; }
    public int F { get; set; }
}

static class Program
{
    static void RunTest(List data, string caption)
    {
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
        GC.WaitForFullGCComplete();
        Stopwatch watch = Stopwatch.StartNew();
        for (int i = 0; i < 500; i++)
        {
            data.ToDataTable();
        }
        watch.Stop();
        Console.WriteLine(caption + "\t" + watch.ElapsedMilliseconds);
    }
    static void Main()
    {
        List foos = new List();
        for (int i = 0 ; i < 5000 ; i++ ){
            foos.Add(new MyData
            { // just gibberish...
                A = i,
                B = i.ToString(),
                C = DateTime.Now.AddSeconds(i),
                D = i,
                E = "hello",
                F = i * 2
            });
        }
        RunTest(foos, "Vanilla");
        Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(
            typeof(MyData));
        RunTest(foos, "Hyper");
        Console.ReadLine(); // return to exit        
    }
}

@Ellesedil我努力记住要明确地披露这些事情,但是因为我不是*卖*任何东西(而是在免费提供许多小时的工作)我承认我在这里感觉不到大量的*内疚. . (8认同)

那么"按原样",它将与反射一样快.如果启用HyperDescriptor,它会反射掉反射...我会快速测试......(2分钟) (4认同)

@MarcGravell是的我会对Expression解决方案很感兴趣.对于需要快速+学习效果的东西.谢谢马克! (3认同)

您的方法ToDataTable不支持可为空的字段:其他信息:DataSet不支持System.Nullable <>。 (2认同)


Mary Hamlin.. 222

我不得不修改Mark Gravell的示例代码来处理可空类型和空值.我在下面列出了一个工作版本.谢谢马克.

public static DataTable ToDataTable(this IList data)
{
    PropertyDescriptorCollection properties = 
        TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    foreach (PropertyDescriptor prop in properties)
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
    foreach (T item in data)
    {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
             row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        table.Rows.Add(row);
    }
    return table;
}

要实现@Jim Beam,请更改方法签名以接受GroupBy的返回:`public static DataTable ToDataTable <TKey,T>(此IEnumerable <IGrouping <TKey,T >>数据)`然后,在foreach循环:`table.Columns.Add(“ Key”,Nullable.GetUnderlyingType(typeof(TKey))?? typeof(TKey));`然后在数据循环周围添加一个循环,在其中迭代组:foreach(IGrouping数据中的<TKey,T>组){foreach(group.Items中的T项目){有关详细信息,请参见此GIST:https://gist.github.com/rickdailey/8679306 (2认同)


A.Baudouin.. 13

这是解决方案的简单组合.它适用于Nullable类型.

public static DataTable ToDataTable(this IList list)
{
  PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
  DataTable table = new DataTable();
  for (int i = 0; i < props.Count; i++)
  {
    PropertyDescriptor prop = props[i];
    table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
  }
  object[] values = new object[props.Count];
  foreach (T item in list)
  {
    for (int i = 0; i < values.Length; i++)
      values[i] = props[i].GetValue(item) ?? DBNull.Value;
    table.Rows.Add(values);
  }
  return table;
}


Onur Omer.. 12

对Mark的答案进行了一些小改动,使其适用List于数据表等值类型:

public static DataTable ListToDataTable(IList data)
{
    DataTable table = new DataTable();

    //special handling for value types and string
    if (typeof(T).IsValueType || typeof(T).Equals(typeof(string)))
    {

        DataColumn dc = new DataColumn("Value");
        table.Columns.Add(dc);
        foreach (T item in data)
        {
            DataRow dr = table.NewRow();
            dr[0] = item;
            table.Rows.Add(dr);
        }
    }
    else
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
        foreach (PropertyDescriptor prop in properties)
        {
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        }
        foreach (T item in data)
        {
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
            {
                try
                {
                    row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                }
                catch (Exception ex)
                {
                    row[prop.Name] = DBNull.Value;
                }
            }
            table.Rows.Add(row);
        }
    }
    return table;
}


Jürgen Stein.. 9

MSDN上的这个链接值得一看:如何:实现CopyToDataTable 通用类型T不是DataRow

这会添加一个扩展方法,允许您执行此操作:

// Create a sequence. 
Item[] items = new Item[] 
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"}, 
  new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
  new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
  new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// Query for items with price greater than 9.99.
var query = from i in items
             where i.Price > 9.99
             orderby i.Price
             select i;

// Load the query results into new DataTable.
DataTable table = query.CopyToDataTable();


小智.. 7

public DataTable ConvertToDataTable(IList data)
{
    PropertyDescriptorCollection properties =
        TypeDescriptor.GetProperties(typeof(T));

    DataTable table = new DataTable();

    foreach (PropertyDescriptor prop in properties)
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);

    foreach (T item in data)
    {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
        {
           row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        }
        table.Rows.Add(row);
    }
    return table;
}

尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以提高其长期价值。 (3认同)


kostas ch... 7

上面是另一种方法:

  List lst = getdata();
  string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst);
  DataTable pDt = JsonConvert.DeserializeObject(json);


Sadegh.. 6

尝试这个

public static DataTable ListToDataTable(IList lst)
{

    currentDT = CreateTable();

    Type entType = typeof(T);

    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
    foreach (T item in lst)
    {
        DataRow row = currentDT.NewRow();
        foreach (PropertyDescriptor prop in properties)
        {

            if (prop.PropertyType == typeof(Nullable) || prop.PropertyType == typeof(Nullable) || prop.PropertyType == typeof(Nullable))
            {
                if (prop.GetValue(item) == null)
                    row[prop.Name] = 0;
                else
                    row[prop.Name] = prop.GetValue(item);
            }
            else
                row[prop.Name] = prop.GetValue(item);                    

        }
        currentDT.Rows.Add(row);
    }

    return currentDT;
}

public static DataTable CreateTable()
{
    Type entType = typeof(T);
    DataTable tbl = new DataTable(DTName);
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
    foreach (PropertyDescriptor prop in properties)
    {
        if (prop.PropertyType == typeof(Nullable))
             tbl.Columns.Add(prop.Name, typeof(decimal));
        else if (prop.PropertyType == typeof(Nullable))
            tbl.Columns.Add(prop.Name, typeof(int));
        else if (prop.PropertyType == typeof(Nullable))
            tbl.Columns.Add(prop.Name, typeof(Int64));
        else
             tbl.Columns.Add(prop.Name, prop.PropertyType);
    }
    return tbl;
}


Johannes Rud.. 5

我自己写了一个小型图书馆来完成这项任务.它仅在第一次将对象类型转换为数据表时使用反射.它会发出一个方法来完成翻译对象类型的所有工作.

它的速度非常快.你可以在这里找到它: GoogleCode上的ModelShredder



1> Marc Gravell..:

这是使用NuGet的FastMember进行的 2013年更新:

IEnumerable data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data)) {
    table.Load(reader);
}

这使用FastMember的元编程API来获得最佳性能.如果您想将其限制为特定成员(或强制执行订单),那么您也可以这样做:

IEnumerable data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data, "Id", "Name", "Description")) {
    table.Load(reader);
}

编辑的Dis/claimer : FastMember是一个Marc Gravell项目.它的黄金和全飞!


是的,这几乎是完全相反的这一个; 反射就足够了 - 或者如果你需要更快,HyperDescriptor在2.0,或者Expression在3.5.实际上,HyperDescriptor应该绰绰有余.

例如:

// remove "this" if not on C# 3.0 / .NET 3.5
public static DataTable ToDataTable(this IList data)
{
    PropertyDescriptorCollection props =
        TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    for(int i = 0 ; i < props.Count ; i++)
    {
        PropertyDescriptor prop = props[i];
        table.Columns.Add(prop.Name, prop.PropertyType);
    }
    object[] values = new object[props.Count];
    foreach (T item in data)
    {
        for (int i = 0; i < values.Length; i++)
        {
            values[i] = props[i].GetValue(item);
        }
        table.Rows.Add(values);
    }
    return table;        
}

现在使用一行,您可以比反射快许多倍(通过启用HyperDescriptor对象类型T).


编辑重新性能查询; 这是一个测试台,结果如下:

Vanilla 27179
Hyper   6997

我怀疑瓶颈已从成员访问转移到DataTable性能......我怀疑你会在那方面有所改进......

码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
public class MyData
{
    public int A { get; set; }
    public string B { get; set; }
    public DateTime C { get; set; }
    public decimal D { get; set; }
    public string E { get; set; }
    public int F { get; set; }
}

static class Program
{
    static void RunTest(List data, string caption)
    {
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
        GC.WaitForFullGCComplete();
        Stopwatch watch = Stopwatch.StartNew();
        for (int i = 0; i < 500; i++)
        {
            data.ToDataTable();
        }
        watch.Stop();
        Console.WriteLine(caption + "\t" + watch.ElapsedMilliseconds);
    }
    static void Main()
    {
        List foos = new List();
        for (int i = 0 ; i < 5000 ; i++ ){
            foos.Add(new MyData
            { // just gibberish...
                A = i,
                B = i.ToString(),
                C = DateTime.Now.AddSeconds(i),
                D = i,
                E = "hello",
                F = i * 2
            });
        }
        RunTest(foos, "Vanilla");
        Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(
            typeof(MyData));
        RunTest(foos, "Hyper");
        Console.ReadLine(); // return to exit        
    }
}


@Ellesedil我努力记住要明确地披露这些事情,但是因为我不是*卖*任何东西(而是在免费提供许多小时的工作)我承认我在这里感觉不到大量的*内疚. .
那么"按原样",它将与反射一样快.如果启用HyperDescriptor,它会反射掉反射...我会快速测试......(2分钟)
@MarcGravell是的我会对Expression解决方案很感兴趣.对于需要快速+学习效果的东西.谢谢马克!
您的方法ToDataTable不支持可为空的字段:其他信息:DataSet不支持System.Nullable <>。

2> Mary Hamlin..:

我不得不修改Mark Gravell的示例代码来处理可空类型和空值.我在下面列出了一个工作版本.谢谢马克.

public static DataTable ToDataTable(this IList data)
{
    PropertyDescriptorCollection properties = 
        TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    foreach (PropertyDescriptor prop in properties)
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
    foreach (T item in data)
    {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
             row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        table.Rows.Add(row);
    }
    return table;
}


要实现@Jim Beam,请更改方法签名以接受GroupBy的返回:`public static DataTable ToDataTable <TKey,T>(此IEnumerable <IGrouping <TKey,T >>数据)`然后,在foreach循环:`table.Columns.Add(“ Key”,Nullable.GetUnderlyingType(typeof(TKey))?? typeof(TKey));`然后在数据循环周围添加一个循环,在其中迭代组:foreach(IGrouping数据中的<TKey,T>组){foreach(group.Items中的T项目){有关详细信息,请参见此GIST:https://gist.github.com/rickdailey/8679306

3> A.Baudouin..:

这是解决方案的简单组合.它适用于Nullable类型.

public static DataTable ToDataTable(this IList list)
{
  PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
  DataTable table = new DataTable();
  for (int i = 0; i < props.Count; i++)
  {
    PropertyDescriptor prop = props[i];
    table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
  }
  object[] values = new object[props.Count];
  foreach (T item in list)
  {
    for (int i = 0; i < values.Length; i++)
      values[i] = props[i].GetValue(item) ?? DBNull.Value;
    table.Rows.Add(values);
  }
  return table;
}



4> Onur Omer..:

对Mark的答案进行了一些小改动,使其适用List于数据表等值类型:

public static DataTable ListToDataTable(IList data)
{
    DataTable table = new DataTable();

    //special handling for value types and string
    if (typeof(T).IsValueType || typeof(T).Equals(typeof(string)))
    {

        DataColumn dc = new DataColumn("Value");
        table.Columns.Add(dc);
        foreach (T item in data)
        {
            DataRow dr = table.NewRow();
            dr[0] = item;
            table.Rows.Add(dr);
        }
    }
    else
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
        foreach (PropertyDescriptor prop in properties)
        {
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        }
        foreach (T item in data)
        {
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
            {
                try
                {
                    row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                }
                catch (Exception ex)
                {
                    row[prop.Name] = DBNull.Value;
                }
            }
            table.Rows.Add(row);
        }
    }
    return table;
}



5> Jürgen Stein..:

MSDN上的这个链接值得一看:如何:实现CopyToDataTable 通用类型T不是DataRow

这会添加一个扩展方法,允许您执行此操作:

// Create a sequence. 
Item[] items = new Item[] 
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"}, 
  new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
  new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
  new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// Query for items with price greater than 9.99.
var query = from i in items
             where i.Price > 9.99
             orderby i.Price
             select i;

// Load the query results into new DataTable.
DataTable table = query.CopyToDataTable();



6> 小智..:
public DataTable ConvertToDataTable(IList data)
{
    PropertyDescriptorCollection properties =
        TypeDescriptor.GetProperties(typeof(T));

    DataTable table = new DataTable();

    foreach (PropertyDescriptor prop in properties)
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);

    foreach (T item in data)
    {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
        {
           row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        }
        table.Rows.Add(row);
    }
    return table;
}


尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以提高其长期价值。

7> kostas ch...:

上面是另一种方法:

  List lst = getdata();
  string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst);
  DataTable pDt = JsonConvert.DeserializeObject(json);



8> Sadegh..:

尝试这个

public static DataTable ListToDataTable(IList lst)
{

    currentDT = CreateTable();

    Type entType = typeof(T);

    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
    foreach (T item in lst)
    {
        DataRow row = currentDT.NewRow();
        foreach (PropertyDescriptor prop in properties)
        {

            if (prop.PropertyType == typeof(Nullable) || prop.PropertyType == typeof(Nullable) || prop.PropertyType == typeof(Nullable))
            {
                if (prop.GetValue(item) == null)
                    row[prop.Name] = 0;
                else
                    row[prop.Name] = prop.GetValue(item);
            }
            else
                row[prop.Name] = prop.GetValue(item);                    

        }
        currentDT.Rows.Add(row);
    }

    return currentDT;
}

public static DataTable CreateTable()
{
    Type entType = typeof(T);
    DataTable tbl = new DataTable(DTName);
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
    foreach (PropertyDescriptor prop in properties)
    {
        if (prop.PropertyType == typeof(Nullable))
             tbl.Columns.Add(prop.Name, typeof(decimal));
        else if (prop.PropertyType == typeof(Nullable))
            tbl.Columns.Add(prop.Name, typeof(int));
        else if (prop.PropertyType == typeof(Nullable))
            tbl.Columns.Add(prop.Name, typeof(Int64));
        else
             tbl.Columns.Add(prop.Name, prop.PropertyType);
    }
    return tbl;
}



9> Johannes Rud..:

我自己写了一个小型图书馆来完成这项任务.它仅在第一次将对象类型转换为数据表时使用反射.它会发出一个方法来完成翻译对象类型的所有工作.

它的速度非常快.你可以在这里找到它: GoogleCode上的ModelShredder



10> Mithir..:

它也可以通过XmlSerialization.我们的想法是 - 序列化为XML,然后读取DataSet的readXml方法.

我用这个代码(来自SO的答案,忘了哪里)

    public static string SerializeXml(T value) where T : class
{
    if (value == null)
    {
        return null;
    }

    XmlSerializer serializer = new XmlSerializer(typeof(T));

    XmlWriterSettings settings = new XmlWriterSettings();

    settings.Encoding = new UnicodeEncoding(false, false);
    settings.Indent = false;
    settings.OmitXmlDeclaration = false;
    // no BOM in a .NET string

    using (StringWriter textWriter = new StringWriter())
    {
        using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
        {
            serializer.Serialize(xmlWriter, value);
        }
        return textWriter.ToString();
    }
}

所以它就像这样简单:

        string xmlString = Utility.SerializeXml(trans.InnerList);

    DataSet ds = new DataSet("New_DataSet");
    using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
    { 
        ds.Locale = System.Threading.Thread.CurrentThread.CurrentCulture;
        ds.ReadXml(reader); 
    }

不确定它如何反对这篇文章的所有其他答案,但它也是一种可能性.



11> Craig Gjerdi..:

马克·格雷夫(Marc Gravell)的答案,但在VB.NET中

Public Shared Function ToDataTable(Of T)(data As IList(Of T)) As DataTable
    Dim props As PropertyDescriptorCollection = TypeDescriptor.GetProperties(GetType(T))
    Dim table As New DataTable()
    For i As Integer = 0 To props.Count - 1
            Dim prop As PropertyDescriptor = props(i)
            table.Columns.Add(prop.Name, prop.PropertyType)
    Next
    Dim values As Object() = New Object(props.Count - 1) {}
    For Each item As T In data
            For i As Integer = 0 To values.Length - 1
                    values(i) = props(i).GetValue(item)
            Next
            table.Rows.Add(values)
    Next
    Return table
End Function

推荐阅读
php
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有