使用反射从类创建数据表?

Posted

技术标签:

【中文标题】使用反射从类创建数据表?【英文标题】: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。请不要重新发明***。 好的,所以我弄错了泛型,但我仍然想做的是从现有类中创建动态数据表 【参考方案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++;
        
    

【讨论】:

以上是关于使用反射从类创建数据表?的主要内容,如果未能解决你的问题,请参考以下文章

使用反射检测属性的访问修饰符类型

尝试在 Google 表格中使用 XML 从类的最后一个实例中获取数据

尝试从类中读取数据但返回 null 而不是其实际值 - 使用 Flutter Provider

使用 Java 从类数组创建类的新实例

在Android中,如何将数据从类传递到相应的布局/片段文件?

vue-class-component:如何创建和初始化反射数据