【问题标题】:Convert generic List/Enumerable to DataTable?将通用 List/Ienumerable 转换为 DataTable?
【发布时间】:2010-10-08 12:57:54
【问题描述】:

我有几个方法可以返回不同的通用列表。

.net 中是否存在任何类静态方法或任何将任何列表转换为数据表的方法?我唯一能想到的就是使用反射来做到这一点。

如果我有这个:

List<Whatever> whatever = new List<Whatever>();

(下一个代码当然不起作用,但我希望有这样的可能性:

DataTable dt = (DataTable) whatever;

【问题讨论】:

  • 当然,一个很好的问题是“为什么?” - 当 List 在许多情况下是比 DataTable 更好的工具时;-p 每个人都有自己的想法,我猜...
  • 我认为这个问题可能是这个问题的重复:stackoverflow.com/questions/523153/… 它甚至有一个几乎相同的答案。 :-)
  • @MarcGravell:我的“为什么?”是 List 操作(遍历列和行)。我正在尝试从 List 进行透视并通过反射访问属性,这很痛苦。我做错了吗?
  • @Eduardo 有许多工具可以消除那里的反射痛苦——FastMember 跃入脑海。 可能 DataTable 对特定场景很有用 - 这完全取决于上下文。也许最大的问题是人们仅仅因为它存在而使用 DataTable 来存储所有数据,而没有花时间考虑选项及其场景。
  • @EduardoMolteni,如果您有兴趣,我更新了 FastMember 以获得对此的直接支持 - 请参阅更新后的答案

标签: c# list generics datatable


【解决方案1】:

这是一个不错的 2013 年更新,使用来自 NuGet 的 FastMember

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

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

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

编辑的Dis/声明:FastMember 是一个 Marc Gravell 项目。这是黄金和全飞!


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

例如:

// remove "this" if not on C# 3.0 / .NET 3.5
public static DataTable ToDataTable<T>(this IList<T> 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;        
}

现在只需一行,您就可以使这比反射快很多倍(通过为对象类型 T 启用 HyperDescriptor)。


编辑重新性能查询;这是一个带有结果的测试台:

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<MyData> 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<MyData> foos = new List<MyData>();
        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        
    }
}

【讨论】:

  • 嗯,“原样”,它会和反思一样快。如果您启用 HyperDescriptor,它会自动反省...我将运行一个快速测试...(2 分钟)
  • @MarcGravell 是的,我会对 Expression 解决方案非常感兴趣。对于需要快速+学习效果的东西。谢谢你,马克!
  • 可能值得一提的是,您是 FastMember 的作者,以提高透明度。您的编辑就像您碰巧遇到的现在可用的这个很棒的软件包一样。
  • @Ellesedil 我努力记住要明确披露这些事情,但由于我没有出售任何东西(而是免费提供许多小时的工作)我承认我不要在这里感到巨大的内疚...
  • 您的方法 ToDataTable 不支持可为空的字段:附加信息:DataSet 不支持 System.Nullable。
【解决方案2】:

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

public static DataTable ToDataTable<T>(this IList<T> 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&lt;TKey, T&gt;(this IEnumerable&lt;IGrouping&lt;TKey, T&gt;&gt; data) 然后,在 foreach 循环之前添加一个额外的列:table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); 然后在周围添加一个循环迭代组的数据循环: foreach (IGrouping group in data) { foreach (T item in group.Items) { 有关详细信息,请参阅此 GIST:gist.github.com/rickdailey/8679306
  • 嘿,有没有办法处理带有内部对象的对象?我只希望内部属性显示为父对象列之后的列
  • @heyNow,我确定有。但是我真的不需要我正在做的那个功能,所以把它留给其他人来扩展。 :)
  • 这是一篇旧帖子,所以不确定这条评论有多大用处,但这个 ToDataTable 方法中有一个鬼鬼祟祟的错误。如果T 实现了接口typeof(T) 可能会返回接口类型而不是对象的实际类,从而导致DataTable 为空。用data.First().GetType() 替换它应该可以解决它。
【解决方案3】:

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

public static DataTable ToDataTable<T>(this IList<T> 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;
}

【讨论】:

  • 这个方案容易出错,因为它依赖于T类中的属性声明顺序。
【解决方案4】:

Marc's answer 进行小改动,使其能够与List&lt;string&gt; 等值类型一起使用到数据表:

public static DataTable ListToDataTable<T>(IList<T> 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", typeof(T));
        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;
}

【讨论】:

  • 如何为 List 制作它?
  • 上述方法也适用于 int (和其他值类型)... int 是一种值类型。见:msdn.microsoft.com/en-us/library/s1ax56ch.aspx
  • 我喜欢这个,因为它不依赖于使用扩展方法。适用于可能无法访问扩展方法的旧代码库。
【解决方案5】:

MSDN上这个链接值得一看:How to: Implement CopyToDataTable<T> Where the Generic Type T Is Not a 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();

【讨论】:

  • @PaulWilliams 谢谢,到目前为止,我使用此代码多年没有问题。但是由于我没有从微软复制示例代码并且只链接到该网站,因此其他解决方案至少更符合答案最佳实践stackoverflow.com/help/how-to-answer
【解决方案6】:

另一种方法是上面的:

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

【讨论】:

  • 非常非常好......但它抛出了 'System.OutOfMemoryException' 类型的异常。我用它处理了 500 000 件物品......但是谢谢你。
  • 这是迄今为止我在网上找到的最干净的解决方案。干得好!
  • 注意DataTable的数据类型与List的数据类型不同。例如:对象中的小数在数据表中是双精度的。
【解决方案7】:
public DataTable ConvertToDataTable<T>(IList<T> 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;
}

【讨论】:

  • 这个方案容易出错,因为它依赖于T类中的属性声明顺序。
【解决方案8】:

试试这个

public static DataTable ListToDataTable<T>(IList<T> lst)
{

    currentDT = CreateTable<T>();

    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<decimal>) || prop.PropertyType == typeof(Nullable<int>) || prop.PropertyType == typeof(Nullable<Int64>))
            {
                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<T>()
{
    Type entType = typeof(T);
    DataTable tbl = new DataTable(DTName);
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
    foreach (PropertyDescriptor prop in properties)
    {
        if (prop.PropertyType == typeof(Nullable<decimal>))
             tbl.Columns.Add(prop.Name, typeof(decimal));
        else if (prop.PropertyType == typeof(Nullable<int>))
            tbl.Columns.Add(prop.Name, typeof(int));
        else if (prop.PropertyType == typeof(Nullable<Int64>))
            tbl.Columns.Add(prop.Name, typeof(Int64));
        else
             tbl.Columns.Add(prop.Name, prop.PropertyType);
    }
    return tbl;
}

【讨论】:

  • 无法编译
【解决方案9】:
It's also possible through XmlSerialization.
The idea is - serialize to `XML` and then `readXml` method of `DataSet`.

I use this code (from an answer in SO, forgot where)

        public static string SerializeXml<T>(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();
        }
    }

so then it's as simple as:

            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); 
        }

Not sure how it stands against all the other answers to this post, but it's also a possibility.

【讨论】:

    【解决方案10】:

    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
    

    【讨论】:

      【解决方案11】:
      List<YourModel> data = new List<YourModel>();
      DataTable dataTable = Newtonsoft.Json.JsonConvert.DeserializeObject<DataTable>(Newtonsoft.Json.JsonConvert.SerializeObject(data));
      

      【讨论】:

      • 虽然此代码可以回答问题,但提供有关 如何 和/或 为什么 解决问题的附加上下文将改善答案的长期价值。
      【解决方案12】:

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

      它的速度非常快。你可以在这里找到它:

      【讨论】:

        【解决方案13】:

        如果您使用的是 .NET Core,请使用 2019 年的答案 - 使用 Nuget ToDataTable library。优点:

        免责声明 - 我是 ToDataTable 的作者

        性能 - 我创建了一些 Benchmark .Net 测试并将它们包含在 ToDataTable repo 中。结果如下:

        创建一个 100,000 行的数据表

                                   MacOS         Windows
        Reflection                 818.5 ms      818.3 ms
        FastMember from           1105.5 ms      976.4 ms
         Mark's answer
        Improved FastMember        524.6 ms      456.4 ms
        ToDataTable                449.0 ms      376.5 ms
        

        Marc's answer 中建议的 FastMember 方法的性能似乎比使用反射的 Mary's answer 差,但我 rolled another method 使用 FastMember TypeAccessor,它的性能要好得多。然而,ToDataTable 包的表现却优于很多。

        【讨论】:

        • 我可以将 MoreLinq ToDataTable() 添加到基准测试中吗?
        • 是的,好主意。有兴趣看看它的比较。
        【解决方案14】:

        将通用列表转换为数据表

        使用 Newtonsoft.Json;

        public DataTable GenericToDataTable(IList<T> list)
        {
            var json = JsonConvert.SerializeObject(list);
            DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
            return dt;
        }
        

        【讨论】:

          【解决方案15】:

          我还必须想出一个替代解决方案,因为这里列出的选项都不适用于我的情况。我使用的是返回 IEnumerable 的 IEnumerable 并且无法枚举属性。这成功了:

          // remove "this" if not on C# 3.0 / .NET 3.5
          public static DataTable ConvertToDataTable<T>(this IEnumerable<T> data)
          {
              List<IDataRecord> list = data.Cast<IDataRecord>().ToList();
          
              PropertyDescriptorCollection props = null;
              DataTable table = new DataTable();
              if (list != null && list.Count > 0)
              {
                  props = TypeDescriptor.GetProperties(list[0]);
                  for (int i = 0; i < props.Count; i++)
                  {
                      PropertyDescriptor prop = props[i];
                      table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
                  }
              }
              if (props != null)
              {
                  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) ?? DBNull.Value;
                      }
                      table.Rows.Add(values);
                  }
              }
              return table;
          }
          

          【讨论】:

            【解决方案16】:
              using System;
            using System.Collections.Generic;
            using System.Linq;
            using System.Web;
            using System.Web.UI;
            using System.Web.UI.WebControls;
            using System.Data;
            using System.ComponentModel;
            
            public partial class Default3 : System.Web.UI.Page
            {
                protected void Page_Load(object sender, EventArgs e)
                {
                    DataTable dt = new DataTable();
                    dt = lstEmployee.ConvertToDataTable();
                }
                public static DataTable ConvertToDataTable<T>(IList<T> list) where T : class
                {
                    try
                    {
                        DataTable table = CreateDataTable<T>();
                        Type objType = typeof(T);
                        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(objType);
                        foreach (T item in list)
                        {
                            DataRow row = table.NewRow();
                            foreach (PropertyDescriptor property in properties)
                            {
                                if (!CanUseType(property.PropertyType)) continue;
                                row[property.Name] = property.GetValue(item) ?? DBNull.Value;
                            }
            
                            table.Rows.Add(row);
                        }
                        return table;
                    }
                    catch (DataException ex)
                    {
                        return null;
                    }
                    catch (Exception ex)
                    {
                        return null;
                    }
            
                }
                private static DataTable CreateDataTable<T>() where T : class
                {
                    Type objType = typeof(T);
                    DataTable table = new DataTable(objType.Name);
                    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(objType);
                    foreach (PropertyDescriptor property in properties)
                    {
                        Type propertyType = property.PropertyType;
                        if (!CanUseType(propertyType)) continue;
            
                        //nullables must use underlying types
                        if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                            propertyType = Nullable.GetUnderlyingType(propertyType);
                        //enums also need special treatment
                        if (propertyType.IsEnum)
                            propertyType = Enum.GetUnderlyingType(propertyType);
                        table.Columns.Add(property.Name, propertyType);
                    }
                    return table;
                }
            
            
                private static bool CanUseType(Type propertyType)
                {
                    //only strings and value types
                    if (propertyType.IsArray) return false;
                    if (!propertyType.IsValueType && propertyType != typeof(string)) return false;
                    return true;
                }
            }
            

            【讨论】:

              【解决方案17】:

              我意识到这已经关闭了一段时间;但是,我对这个特定问题有一个解决方案,但需要稍微改变一下:列和数据表需要预定义/已经实例化。然后我需要简单地将类型插入数据表中。

              下面是我所做的一个例子:

              public static class Test
              {
                  public static void Main()
                  {
                      var dataTable = new System.Data.DataTable(Guid.NewGuid().ToString());
              
                      var columnCode = new DataColumn("Code");
                      var columnLength = new DataColumn("Length");
                      var columnProduct = new DataColumn("Product");
              
                      dataTable.Columns.AddRange(new DataColumn[]
                          {
                              columnCode,
                              columnLength,
                              columnProduct
                          });
              
                      var item = new List<SomeClass>();
              
                      item.Select(data => new
                      {
                          data.Id,
                          data.Name,
                          data.SomeValue
                      }).AddToDataTable(dataTable);
                  }
              }
              
              static class Extensions
              {
                  public static void AddToDataTable<T>(this IEnumerable<T> enumerable, System.Data.DataTable table)
                  {
                      if (enumerable.FirstOrDefault() == null)
                      {
                          table.Rows.Add(new[] {string.Empty});
                          return;
                      }
              
                      var properties = enumerable.FirstOrDefault().GetType().GetProperties();
              
                      foreach (var item in enumerable)
                      {
                          var row = table.NewRow();
                          foreach (var property in properties)
                          {
                              row[property.Name] = item.GetType().InvokeMember(property.Name, BindingFlags.GetProperty, null, item, null);
                          }
                          table.Rows.Add(row);
                      }
                  }
              }
              

              【讨论】:

              • 你能给我举个例子吗?我如何使用 addtodataTable() 方法的扩展方法
              • 这段代码已经有一个例子——看看 Main() 方法。最后一段代码使用了扩展名。
              • 进一步阅读请看这篇来自MSDN的关于扩展方法的文章:msdn.microsoft.com/en-us/library/bb383977.aspx
              【解决方案18】:

              如果您使用的是 VB.NET,那么这个类就可以完成这项工作。

              Imports System.Reflection
              ''' <summary>
              ''' Convert any List(Of T) to a DataTable with correct column types and converts Nullable Type values to DBNull
              ''' </summary>
              
              Public Class ConvertListToDataset
              
                  Public Function ListToDataset(Of T)(ByVal list As IList(Of T)) As DataTable
              
                      Dim dt As New DataTable()
                      '/* Create the DataTable columns */
                      For Each pi As PropertyInfo In GetType(T).GetProperties()
                          If pi.PropertyType.IsValueType Then
                              Debug.Print(pi.Name)
                          End If
                          If IsNothing(Nullable.GetUnderlyingType(pi.PropertyType)) Then
                              dt.Columns.Add(pi.Name, pi.PropertyType)
                          Else
                              dt.Columns.Add(pi.Name, Nullable.GetUnderlyingType(pi.PropertyType))
                          End If
                      Next
              
                      '/* Populate the DataTable with the values in the Items in List */
                      For Each item As T In list
                          Dim dr As DataRow = dt.NewRow()
                          For Each pi As PropertyInfo In GetType(T).GetProperties()
                              dr(pi.Name) = IIf(IsNothing(pi.GetValue(item)), DBNull.Value, pi.GetValue(item))
                          Next
                          dt.Rows.Add(dr)
                      Next
                      Return dt
              
                  End Function
              
              End Class
              

              【讨论】:

                【解决方案19】:

                如果你的类中有属性,这行代码就可以了!!

                PropertyDescriptorCollection props =
                            TypeDescriptor.GetProperties(typeof(T));
                

                但如果您拥有所有公共字段,请使用:

                public static DataTable ToDataTable<T>(  IList<T> data)
                        {
                        FieldInfo[] myFieldInfo;
                        Type myType = typeof(T);
                        // Get the type and fields of FieldInfoClass.
                        myFieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance
                            | BindingFlags.Public);
                
                        DataTable dt = new DataTable();
                        for (int i = 0; i < myFieldInfo.Length; i++)
                            {
                            FieldInfo property = myFieldInfo[i];
                            dt.Columns.Add(property.Name, property.FieldType);
                            }
                        object[] values = new object[myFieldInfo.Length];
                        foreach (T item in data)
                            {
                            for (int i = 0; i < values.Length; i++)
                                {
                                values[i] = myFieldInfo[i].GetValue(item);
                                }
                            dt.Rows.Add(values);
                            }
                        return dt;
                        }
                

                最初的答案来自上面,我只是编辑使用字段而不是属性

                并使用它来做到这一点

                 DataTable dt = new DataTable();
                            dt = ToDataTable(myBriefs);
                            gridData.DataSource = dt;
                            gridData.DataBind();
                

                【讨论】:

                  【解决方案20】:

                  要将通用列表转换为数据表,您可以使用DataTableGenerator

                  此库可让您将列表转换为具有多种功能的数据表,例如

                  • 翻译数据表头
                  • 指定要显示的列

                  【讨论】:

                    【解决方案21】:
                      private DataTable CreateDataTable(IList<T> item)
                            {
                                Type type = typeof(T);
                                var properties = type.GetProperties();
                    
                                DataTable dataTable = new DataTable();
                                foreach (PropertyInfo info in properties)
                                {
                                    dataTable.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
                                }
                    
                                foreach (T entity in item)
                                {
                                    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;
                            }
                    

                    【讨论】:

                      【解决方案22】:

                      你可以试试下面的方法

                      public static DataTable GetDataTableFromObjects(object[] objects)
                      {
                          if (objects != null && objects.Length > 0)
                          {
                              Type t = objects[0].GetType();
                              DataTable dt = new DataTable(t.Name);
                              foreach (PropertyInfo pi in t.GetProperties())
                              {
                                  dt.Columns.Add(new DataColumn(pi.Name));
                              }
                              foreach (var o in objects)
                              {
                                  DataRow dr = dt.NewRow();
                                  foreach (DataColumn dc in dt.Columns)
                                  {
                                      dr[dc.ColumnName] = o.GetType().GetProperty(dc.ColumnName).GetValue(o, null);
                                  }
                                  dt.Rows.Add(dr);
                              }
                              return dt;
                          }
                          return null;
                      }
                      

                      【讨论】:

                        【解决方案23】:

                        这是将列表转换为数据表的简单控制台应用程序。

                        using System;
                        using System.Collections.Generic;
                        using System.Linq;
                        using System.Text;
                        using System.Data;
                        using System.ComponentModel;
                        
                        namespace ConvertListToDataTable
                        {
                            public static class Program
                            {
                                public static void Main(string[] args)
                                {
                                    List<MyObject> list = new List<MyObject>();
                                    for (int i = 0; i < 5; i++)
                                    {
                                        list.Add(new MyObject { Sno = i, Name = i.ToString() + "-KarthiK", Dat = DateTime.Now.AddSeconds(i) });
                                    }
                        
                                    DataTable dt = ConvertListToDataTable(list);
                                    foreach (DataRow row in dt.Rows)
                                    {
                                        Console.WriteLine();
                                        for (int x = 0; x < dt.Columns.Count; x++)
                                        {
                                            Console.Write(row[x].ToString() + " ");
                                        }
                                    }
                                    Console.ReadLine();
                                }
                        
                                public class MyObject
                                {
                                    public int Sno { get; set; }
                                    public string Name { get; set; }
                                    public DateTime Dat { get; set; }
                                }
                        
                                public static DataTable ConvertListToDataTable<T>(this List<T> iList)
                                {
                                    DataTable dataTable = new DataTable();
                                    PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
                                    for (int i = 0; i < props.Count; i++)
                                    {
                                        PropertyDescriptor propertyDescriptor = props[i];
                                        Type type = propertyDescriptor.PropertyType;
                        
                                        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                                            type = Nullable.GetUnderlyingType(type);
                        
                                        dataTable.Columns.Add(propertyDescriptor.Name, type);
                                    }
                                    object[] values = new object[props.Count];
                                    foreach (T iListItem in iList)
                                    {
                                        for (int i = 0; i < values.Length; i++)
                                        {
                                            values[i] = props[i].GetValue(iListItem);
                                        }
                                        dataTable.Rows.Add(values);
                                    }
                                    return dataTable;
                                }
                            }
                        }
                        

                        【讨论】:

                          【解决方案24】:
                           Dim counties As New List(Of County)
                           Dim dtCounties As DataTable
                           dtCounties = _combinedRefRepository.Get_Counties()
                           If dtCounties.Rows.Count <> 0 Then
                              For Each row As DataRow In dtCounties.Rows
                                Dim county As New County
                                county.CountyId = row.Item(0).ToString()
                                county.CountyName = row.Item(1).ToString().ToUpper()
                                counties.Add(county)
                              Next
                              dtCounties.Dispose()
                           End If
                          

                          【讨论】:

                            【解决方案25】:

                            我认为它更方便易用。

                               List<Whatever> _lobj= new List<Whatever>(); 
                                var json = JsonConvert.SerializeObject(_lobj);
                                            DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
                            

                            【讨论】:

                              【解决方案26】:

                              如果您想使用反射并设置列顺序/仅包含某些列/排除某些列,请尝试以下操作:

                                      private static DataTable ConvertToDataTable<T>(IList<T> data, string[] fieldsToInclude = null,
                              string[] fieldsToExclude = null)
                                  {
                                      PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
                                      DataTable table = new DataTable();
                                      foreach (PropertyDescriptor prop in properties)
                                      {
                                          if ((fieldsToInclude != null && !fieldsToInclude.Contains(prop.Name)) ||
                                              (fieldsToExclude != null && fieldsToExclude.Contains(prop.Name)))
                                              continue;
                                          table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
                                      }
                              
                                      foreach (T item in data)
                                      {
                                          var atLeastOnePropertyExists = false;
                                          DataRow row = table.NewRow();
                                          foreach (PropertyDescriptor prop in properties)
                                          {
                              
                                              if ((fieldsToInclude != null && !fieldsToInclude.Contains(prop.Name)) ||
                              (fieldsToExclude != null && fieldsToExclude.Contains(prop.Name)))
                                                  continue;
                              
                                              row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                                              atLeastOnePropertyExists = true;
                                          }
                              
                                          if(atLeastOnePropertyExists) table.Rows.Add(row);
                                      }
                              
                              
                                      if (fieldsToInclude != null)
                                          SetColumnsOrder(table, fieldsToInclude);
                              
                                      return table;
                              
                                  }
                              
                                  private static void SetColumnsOrder(DataTable table, params String[] columnNames)
                                  {
                                      int columnIndex = 0;
                                      foreach (var columnName in columnNames)
                                      {
                                          table.Columns[columnName].SetOrdinal(columnIndex);
                                          columnIndex++;
                                      }
                                  }
                              

                              【讨论】:

                                【解决方案27】:
                                List<object> Basket;
                                
                                string json = JsonConvert.SerializeObject(Basket, Formatting.Indented);
                                DataTable dtUsingMethod = Business.GetJSONToDataTableUsingNewtonSoftDll(json);
                                
                                
                                
                                public static DataTable GetJSONToDataTableUsingNewtonSoftDll(string JSONData)
                                {
                                    DataTable dt = (DataTable)JsonConvert.DeserializeObject(JSONData, (typeof(DataTable)));
                                    return dt;
                                }
                                

                                【讨论】:

                                • 您能否进一步解释您的代码在做什么以及是否需要任何依赖项(如 NewtonSoft)?
                                【解决方案28】:

                                这是列表中的另一个。 Cinchoo ETL - 一个将枚举转换为数据表的开源库。

                                List<Whatever> whatever = new List<Whatever>();
                                var dt = whatever.AsDataTable();
                                

                                免责声明:我是这个库的作者。

                                【讨论】:

                                  猜你喜欢
                                  • 1970-01-01
                                  • 2012-02-24
                                  • 2011-03-24
                                  • 1970-01-01
                                  • 1970-01-01
                                  • 2011-09-30
                                  • 2018-08-04
                                  • 1970-01-01
                                  • 2011-01-17
                                  相关资源
                                  最近更新 更多