it-swarm.com.de

Verwenden von Reflection zum Erstellen einer DataTable aus einer Klasse?

Ich habe gerade etwas über Generics gelernt und frage mich, ob ich damit Daten aus meinen Klassen dynamisch erstellen kann.

Oder ich vermisse den Punkt hier ... Hier ist mein Code. Ich versuche, eine Datentabelle aus meiner vorhandenen Klasse zu erstellen und zu füllen. Ich bleibe jedoch in meinem Gedankengang stecken.

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?


        }      
    }
}    

Jetzt am DataTable-Punkt ist es fehlerhaft. Wie kann ich die Daten außerdem mit der Killer-Klasse füllen?

24
vwdewaal

Aufbauend auf allen vorherigen Antworten, hier eine Version, die aus jeder Sammlung eine DataTable erstellt:

public static DataTable CreateDataTable<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, 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;
}
69

meine liebste hausgemachte Funktion. es schaffen und bevölkern alle gleichzeitig. werfen Sie einen Gegenstand.

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

Der Fehler kann durch eine Änderung behoben werden:

dogTable = CreateDataTable(Dog);

zu diesem:

dogTable = CreateDataTable(typeof(Dog));

Es gibt jedoch einige Einschränkungen bei dem, was Sie zu tun versuchen. Erstens kann DataTable keine komplexen Typen speichern. Wenn also Dog eine Instanz von Cat enthält, können Sie diese nicht als Spalte hinzufügen. Es liegt an Ihnen, was Sie in diesem Fall tun möchten, aber denken Sie daran.

Zweitens würde ich empfehlen, dass Sie nur dann eine DataTable verwenden, wenn Sie Code erstellen, der nichts über die verbrauchenden Daten weiß. Hierfür gibt es gültige Anwendungsfälle (z. B. ein benutzergesteuertes Data Mining-Tool). Wenn Sie die Daten bereits in der Dog-Instanz haben, verwenden Sie sie einfach.

Noch ein kleiner Leckerbissen:

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

kann zu diesem verdichtet werden:

DataTable dogTable = CreateDataTable(Dog);
4
Mike Perrenoud

Hier ist eine kompaktere Version von Davids Antwort die auch eine Erweiterungsfunktion ist. Ich habe den Code in einem C # -Projekt auf Github gepostet. 

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

Ich habe festgestellt, dass dies sehr gut in Verbindung mit Code funktioniert, um eine DataTable in CSV zu schreiben. 

4
cdiggins

Hier ist eine VB.Net-Version, die eine Datentabelle aus einer generischen Liste erstellt, die der Funktion als Objekt übergeben wird. Es gibt auch eine Hilfsfunktion (ObjectToDataTable), die eine Datentabelle aus einem Objekt erstellt.

Importiert 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
1
Michael Bross

Hier ist ein bisschen modifizierter Code, der das Problem mit der Zeitzone für datetime-Felder beseitigte:

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

sie können das Objekt in XML konvertieren, das XML-Dokument in ein Dataset laden und dann die erste Tabelle aus dem Datensatz extrahieren. Ich verstehe jedoch nicht, wie praktisch dies sein kann, da es die Erstellung von Streams, Datasets und Datatables und die Verwendung von Konvertierungen zum Erstellen des XML-Dokuments berücksichtigt.

Ich denke, für den Beweis des Konzepts kann ich verstehen, warum. Hier ist ein Beispiel, aber etwas zurückhaltend.

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

        }      
    }
}

siehe folgendes Beispiel hier

Mit der Antwort von @neoistheone habe ich die folgenden Abschnitte geändert. Funktioniert jetzt gut.

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();
        }
0
vwdewaal