web-dev-qa-db-fra.com

Vous utilisez Reflection pour créer un DataTable à partir d’une classe?

Je viens tout juste d'apprendre les génériques et je me demande si je peux l'utiliser pour construire dynamiquement des tables de données à partir de mes classes.

Ou peut-être que je manque le point ici… .. Voici mon code, ce que j'essaie de faire est de créer un datatable de ma classe existante et de le remplir. Cependant, je suis coincé dans mon processus de pensée.

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?


        }      
    }
}    

Maintenant, au point DataTable, il se trompe. De plus, étant nouveau pour la réflexion et les génériques, comment vais-je réellement renseigner les données avec la classe Killer?

24
vwdewaal

Construisant toutes les réponses précédentes, voici une version qui crée un DataTable à partir de n'importe quelle collection:

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
David Airapetyan

ma fonction maison préférée. il crée et remplit tout en même temps. jeter n'importe quel objet.

 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

L'erreur peut être résolue en modifiant ceci:

dogTable = CreateDataTable(Dog);

pour ça:

dogTable = CreateDataTable(typeof(Dog));

Mais il y a des mises en garde avec ce que vous essayez de faire. Tout d'abord, un DataTable ne peut pas stocker de types complexes. Par conséquent, si Dog a une instance de Cat, vous ne pourrez pas l'ajouter sous forme de colonne. C’est à vous de décider ce que vous voulez faire dans ce cas, mais gardez cela à l’esprit.

Deuxièmement, je recommanderais que la seule fois où vous utilisez une DataTable est lorsque vous créez un code ne connaissant que rien sur les données qu'il consomme. Il existe des cas d'utilisation valables pour cela (par exemple, un outil d'exploration de données piloté par l'utilisateur). Si vous avez déjà les données dans l'instance Dog, utilisez-les simplement.

Une autre petite friandise, ceci:

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

peut être condensé à ceci:

DataTable dogTable = CreateDataTable(Dog);
4
Mike Perrenoud

Voici une version plus compacte de la réponse de David qui est également une fonction d’extension. J'ai posté le code dans un projet C # sur 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;
    }     
}

J'ai trouvé que cela fonctionne très bien avec le code pour écrire un DataTable au format CSV

4
cdiggins

Voici une version VB.Net qui crée une table de données à partir d'une liste générique transmise à la fonction en tant qu'objet. Il existe également une fonction d'assistance (ObjectToDataTable) qui crée une table de données à partir d'un objet.

Importations 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

Voici un peu le code modifié, qui fixe le problème de fuseau horaire pour les champs datetime:

    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
Krasimir Slaveykov

vous pouvez convertir l'objet en XML, puis charger le document XML dans un ensemble de données, puis extraire le premier tableau de l'ensemble de données. Cependant, je ne vois pas comment cela pourrait être pratique car cela implique la création de flux, d'ensembles de données et de tables de données et l'utilisation de conversations pour créer le document XML.

Je suppose que pour preuve de concept, je peux comprendre pourquoi. Voici un exemple, mais quelque peu hésitant à l'utiliser.

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

        }      
    }
}

regardez l'exemple suivant ici

0

En utilisant la réponse fournie par @neoistheone, j’ai modifié les sections suivantes. Fonctionne bien maintenant.

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