【问题标题】:Using Reflection to create a DataTable from a Class?使用反射从类创建数据表?
【发布时间】:2013-09-15 18:22:14
【问题描述】:

我刚刚了解了泛型,我想知道是否可以使用它从我的类中动态构建数据表。

或者我可能错过了这里的重点。 这是我的代码,我想做的是从我现有的类创建一个数据表并填充它。但是我陷入了思考过程。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data;

namespace Generics
{
    public class Dog
    {
        public string Breed { get; set; }
        public string Name { get; set; }
        public int legs { get; set; }
        public bool tail { get; set; }
    }

    class Program
    {
        public static DataTable CreateDataTable(Type animaltype)
        {
            DataTable return_Datatable = new DataTable();
            foreach (PropertyInfo info in animaltype.GetProperties())
            {
                return_Datatable.Columns.Add(new DataColumn(info.Name, info.PropertyType));
            }
            return return_Datatable;
        }

        static void Main(string[] args)
        {
            Dog Killer = new Dog();
            Killer.Breed = "Maltese Poodle";
            Killer.legs = 3;
            Killer.tail = false;
            Killer.Name = "Killer";

            DataTable dogTable = new DataTable();
            dogTable = CreateDataTable(Dog);
//How do I continue from here?


        }      
    }
}    

现在在DataTable 点出现错误。 另外,作为反射和泛型的新手,我将如何使用 Killer 类实际填充数据?

【问题讨论】:

  • 好的,所以你实际上并没有使用泛型,你得到了什么错误?
  • 我不是?哎呀。错误 2 'Generics.Program.CreateDataTable(System.Type)' 的最佳重载方法匹配有一些无效参数 错误 3 参数 1:无法从 'Generics.Dog' 转换为 'System.Type' 错误 1 ​​'Generics.Dog'是“类型”,但用作“变量”
  • Generics + DataBase = Entity Framework。请不要重新发明轮子。
  • 好的,所以我弄错了泛型,但我仍然想做的是从现有类中创建动态数据表

标签: c# .net oop reflection


【解决方案1】:

这个错误可以通过改变来解决:

dogTable = CreateDataTable(Dog);

到这里:

dogTable = CreateDataTable(typeof(Dog));

但是您要尝试执行的操作有一些注意事项。首先,DataTable 不能存储复杂类型,因此如果Dog 上有Cat 的实例,您将无法将其添加为列。在这种情况下,您想做什么取决于您,但请记住这一点。

其次,我建议您使用DataTable 的唯一时间是在您构建的代码对所使用的数据一无所知时。对此有有效的用例(例如,用户驱动的数据挖掘工具)。如果Dog 实例中已有数据,请使用它。

另一个小花絮,这个:

DataTable dogTable = new DataTable();
dogTable = CreateDataTable(Dog);

可以浓缩成这样:

DataTable dogTable = CreateDataTable(Dog);

【讨论】:

    【解决方案2】:

    使用@neoistheone 提供的答案,我更改了以下部分。现在工作正常。

    DataTable dogTable = new DataTable();
            dogTable = CreateDataTable(typeof(Dog));
    
            dogTable.Rows.Add(Killer.Breed, Killer.Name,Killer.legs,Killer.tail);
    
            foreach (DataRow row in dogTable.Rows)
            {
                Console.WriteLine(row.Field<string>("Name") + " " + row.Field<string>("Breed"));
                Console.ReadLine();
            }
    

    【讨论】:

      【解决方案3】:

      我最喜欢的自制功能。它同时创建和填充。扔任何物体。

       public static DataTable ObjectToData(object o)
       {
          DataTable dt = new DataTable("OutputData");
      
          DataRow dr = dt.NewRow();
          dt.Rows.Add(dr);
      
          o.GetType().GetProperties().ToList().ForEach(f =>
          {
              try
              {
                  f.GetValue(o, null);
                  dt.Columns.Add(f.Name, f.PropertyType);
                  dt.Rows[0][f.Name] = f.GetValue(o, null);
              }
              catch { }
          });
          return dt;
       }
      

      【讨论】:

        【解决方案4】:

        在所有之前的答案的基础上,这里是一个从任何集合创建 DataTable 的版本:

        public static DataTable CreateDataTable<T>(IEnumerable<T> list)
        {
            Type type = typeof(T);
            var properties = type.GetProperties();      
            
            DataTable dataTable = new DataTable();
            dataTable.TableName = typeof(T).FullName;
            foreach (PropertyInfo info in properties)
            {
                dataTable.Columns.Add(new DataColumn(info.Name, Nullable.GetUnderlyingType(info.PropertyType) ?? 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;
        }
        

        【讨论】:

        • 太好了,我已经有一年多没有接触过那部分代码了,但这正好赶上我正在做的其他事情。谢谢大卫
        • 修改了代码以合并建议的编辑以使此代码支持可为空的类型。
        • 主类中的嵌套类型呢?你如何把它们弄平
        • 如果嵌套类型不包含重复元素,那么扁平化嵌套类型是有意义的(否则这会变得非常复杂)。这是一种可以将类型展平为其基本属性的方法:gist.github.com/anonymous/aeab3b4f95e6efc0d7130cf91a773bfb
        • 如何为值做同样的事情留给读者练习:)
        【解决方案5】:

        这是一个 VB.Net 版本,它从作为对象传递给函数的通用列表创建数据表。还有一个辅助函数(ObjectToDataTable)可以从一个对象创建一个数据表。

        导入 System.Reflection

           Public Shared Function ListToDataTable(ByVal _List As Object) As DataTable
        
            Dim dt As New DataTable
        
            If _List.Count = 0 Then
                MsgBox("The list cannot be empty. This is a requirement of the ListToDataTable function.")
                Return dt
            End If
        
            Dim obj As Object = _List(0)
            dt = ObjectToDataTable(obj)
            Dim dr As DataRow = dt.NewRow
        
            For Each obj In _List
        
                dr = dt.NewRow
        
                For Each p as PropertyInfo In obj.GetType.GetProperties
        
                    dr.Item(p.Name) = p.GetValue(obj, p.GetIndexParameters)
        
                Next
        
                dt.Rows.Add(dr)
        
            Next
        
            Return dt
        
        End Function
        
        Public Shared Function ObjectToDataTable(ByVal o As Object) As DataTable
        
            Dim dt As New DataTable
            Dim properties As List(Of PropertyInfo) = o.GetType.GetProperties.ToList()
        
            For Each prop As PropertyInfo In properties
        
                dt.Columns.Add(prop.Name, prop.PropertyType)
        
            Next
        
            dt.TableName = o.GetType.Name
        
            Return dt
        
        End Function
        

        【讨论】:

          【解决方案6】:

          这里有一点修改的代码,它修复了数据时间字段的时区问题:

              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++)
                      {
                          if (props[i].PropertyType == typeof(DateTime))
                          {
                              DateTime currDT = (DateTime)props[i].GetValue(item);
                              values[i] = currDT.ToUniversalTime();
                          }
                          else
                          {
                              values[i] = props[i].GetValue(item);
                          }
                      }
                      table.Rows.Add(values);
                  }
                  return table;
              }
          

          【讨论】:

            【解决方案7】:

            您可以将对象转换为 xml,然后将 xml 文档加载到数据集,然后从数据集中提取第一个表。但是我看不出这是如何实用的,因为它推断创建流、数据集和数据表并使用转换来创建 xml 文档。

            我想对于概念证明,我可以理解为什么。这是一个示例,但使用起来有些犹豫。

            using System;
            using System.Collections.Generic;
            using System.Linq;
            using System.Text;
            using System.Reflection;
            using System.Data;
            using System.Xml.Serialization;
            
            namespace Generics
            {
            public class Dog
            {
                public string Breed { get; set; }
                public string Name { get; set; }
                public int legs { get; set; }
                public bool tail { get; set; }
            }
            
            class Program
            {
                public static DataTable CreateDataTable(Object[] arr)
                {
                    XmlSerializer serializer = new XmlSerializer(arr.GetType());
                    System.IO.StringWriter sw = new System.IO.StringWriter();
                    serializer.Serialize(sw, arr);
                    System.Data.DataSet ds = new System.Data.DataSet();
                    System.Data.DataTable dt = new System.Data.DataTable();
                    System.IO.StringReader reader = new System.IO.StringReader(sw.ToString());
            
                    ds.ReadXml(reader);
                    return ds.Tables[0];
                }
            
                static void Main(string[] args)
                {
                    Dog Killer = new Dog();
                    Killer.Breed = "Maltese Poodle";
                    Killer.legs = 3;
                    Killer.tail = false;
                    Killer.Name = "Killer";
            
                    Dog [] array_dog = new Dog[5];
                    Dog [0] = killer;
                    Dog [1] = killer;
                    Dog [2] = killer;
                    Dog [3] = killer;
                    Dog [4] = killer;
            
                    DataTable dogTable = new DataTable();
                    dogTable = CreateDataTable(array_dog);
            
                    // continue here
            
                    }      
                }
            }
            

            看下面的例子here

            【讨论】:

            • 你应该在代码中说明你做了什么或改变了什么。
            【解决方案8】:

            这是一个更紧凑的David's answer 版本,它也是一个扩展函数。我已经在C# project on Github 中发布了代码。

            public static class Extensions
            {
                public static DataTable ToDataTable<T>(this IEnumerable<T> self)
                {
                    var properties = typeof(T).GetProperties();
            
                    var dataTable = new DataTable();
                    foreach (var info in properties)
                        dataTable.Columns.Add(info.Name, Nullable.GetUnderlyingType(info.PropertyType) 
                           ?? info.PropertyType);
            
                    foreach (var entity in self)
                        dataTable.Rows.Add(properties.Select(p => p.GetValue(entity)).ToArray());
            
                    return dataTable;
                }     
            }
            

            我发现这与编写DataTable to CSV 的代码结合使用效果很好。

            【讨论】:

            • 精简版并不意味着更好,我认为它更难阅读,我更喜欢大卫的回答,因为它也更容易调试
            • 真的吗?发现它更容易阅读。
            【解决方案9】:

            如果你想设置列顺序/只包括一些列/排除一些列试试这个:

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

            【讨论】:

              猜你喜欢
              • 2016-12-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-02-14
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多