web-dev-qa-db-ja.com

Reflectionを使用してクラスからDataTableを作成しますか?

ジェネリックについて学んだばかりで、それを使用してクラスからデータテーブルを動的に構築できるかどうか疑問に思っています。

または、ここでポイントが欠落している可能性があります。ここに私のコードがあります。私がやろうとしているのは、既存のクラスからデータテーブルを作成し、それを設定することです。しかし、私は思考プロセスで立ち往生しています。

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クラスを入力するにはどうすればよいですか?

28
vwdewaal

これまでのすべての回答に基づいて、コレクションからDataTableを作成するバージョンを次に示します。

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

私のお気に入りの自家製機能。すべて同時に作成および入力されます。オブジェクトを投げます。

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

David's answer のよりコンパクトなバージョンは次のとおりです。これは拡張機能でもあります。 GithubのC#プロジェクト にコードを投稿しました。

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 を記述するコードと組み合わせて非常にうまく機能することがわかりました。

7
cdiggins

これを変更することでエラーを解決できます。

dogTable = CreateDataTable(Dog);

これに:

dogTable = CreateDataTable(typeof(Dog));

しかし、あなたがやろうとしていることにはいくつかの警告があります。まず、DataTableは複雑な型を格納できないため、DogCatのインスタンスがある場合、列として追加することはできません。その場合に何をしたいかはあなた次第ですが、心に留めておいてください。

第二に、DataTableを使用するのは、消費するデータについてnothingを知っているコードをビルドするときだけにすることをお勧めします。これには有効なユースケースがあります(例:ユーザー主導のデータマイニングツール)。 Dogインスタンスに既にデータがある場合は、それを使用してください。

もう一つのちょっとした話、これ:

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

これに凝縮することができます:

DataTable dogTable = CreateDataTable(Dog);
4
Mike Perrenoud

以下は、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;
    }
3

これは、オブジェクトとして関数に渡された汎用リストからデータテーブルを作成する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
1
Michael Bross

オブジェクトを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

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