Konversi Daftar generik / Enumerable ke DataTable?

261

Saya punya beberapa metode yang mengembalikan Daftar Generik yang berbeda.

Ada di .net setiap metode statis kelas atau apa pun untuk mengubah daftar apa pun menjadi datatable? Satu-satunya hal yang dapat saya bayangkan adalah menggunakan Refleksi untuk melakukan ini.

JIKA saya punya ini:

List<Whatever> whatever = new List<Whatever>();

(Kode berikut ini tidak berfungsi tentu saja, tetapi saya ingin memiliki kemungkinan:

DataTable dt = (DataTable) whatever;
Josema
sumber
2
Tentu saja, pertanyaan yang bagus adalah "mengapa?" - ketika List <T> dalam banyak kasus alat yang lebih baik daripada DataTable ;-p Masing-masing mereka sendiri, saya kira ...
Marc Gravell
1
Saya pikir yang satu ini mungkin merupakan duplikat dari pertanyaan ini: stackoverflow.com/questions/523153/... Itu bahkan memiliki jawaban yang hampir sama. :-)
mezoid
2
@MarcGravell: "mengapa?" adalah manipulasi Daftar <T> (Melintasi kolom & baris). Saya mencoba membuat poros dari Daftar <T> dan mengakses properti melalui refleksi itu menyebalkan. Saya salah melakukannya?
Eduardo Molteni
1
@Eduardo ada sejumlah alat untuk menghilangkan rasa sakit refleksi di sana - FastMember melompat ke pikiran. Ini bisa juga jadi DataTable berguna untuk skenario tertentu - itu semua tergantung pada konteks. Mungkin masalah terbesar adalah orang yang menggunakan DataTable untuk semua penyimpanan data hanya karena itu ada , tanpa meluangkan waktu untuk mempertimbangkan opsi dan skenario mereka.
Marc Gravell
@EduardoMolteni jika Anda tertarik, saya memperbarui FastMember untuk mendapatkan dukungan langsung untuk ini - lihat jawaban yang diperbarui
Marc Gravell

Jawaban:

325

Inilah pembaruan 2013 yang bagus menggunakan FastMember dari NuGet:

IEnumerable<SomeType> data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data)) {
    table.Load(reader);
}

Ini menggunakan API meta-pemrograman FastMember untuk kinerja maksimum. Jika Anda ingin membatasi itu untuk anggota tertentu (atau menegakkan pesanan), maka Anda dapat melakukannya juga:

IEnumerable<SomeType> data = ...
DataTable table = new DataTable();
using(var reader = ObjectReader.Create(data, "Id", "Name", "Description")) {
    table.Load(reader);
}

Editor's Dis / claimer: FastMember adalah proyek Marc Gravell. Emas dan lalat penuh!


Ya, ini persis kebalikan dari yang ini ; refleksi sudah cukup - atau jika Anda perlu lebih cepat, HyperDescriptordalam 2.0, atau mungkin Expressiondalam 3,5. Sebenarnya, HyperDescriptorharus lebih dari cukup.

Sebagai contoh:

// remove "this" if not on C# 3.0 / .NET 3.5
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++)
        {
            values[i] = props[i].GetValue(item);
        }
        table.Rows.Add(values);
    }
    return table;        
}

Sekarang dengan satu baris Anda dapat membuat ini berkali-kali lebih cepat daripada refleksi (dengan mengaktifkan HyperDescriptoruntuk tipe objek T).


edit permintaan kinerja ulang; inilah rig uji dengan hasil:

Vanilla 27179
Hyper   6997

Saya menduga bahwa bottleneck telah bergeser dari akses anggota ke DataTablekinerja ... Saya ragu Anda akan banyak memperbaiki hal itu ...

kode:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
public class MyData
{
    public int A { get; set; }
    public string B { get; set; }
    public DateTime C { get; set; }
    public decimal D { get; set; }
    public string E { get; set; }
    public int F { get; set; }
}

static class Program
{
    static void RunTest(List<MyData> data, string caption)
    {
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers();
        GC.WaitForFullGCComplete();
        Stopwatch watch = Stopwatch.StartNew();
        for (int i = 0; i < 500; i++)
        {
            data.ToDataTable();
        }
        watch.Stop();
        Console.WriteLine(caption + "\t" + watch.ElapsedMilliseconds);
    }
    static void Main()
    {
        List<MyData> foos = new List<MyData>();
        for (int i = 0 ; i < 5000 ; i++ ){
            foos.Add(new MyData
            { // just gibberish...
                A = i,
                B = i.ToString(),
                C = DateTime.Now.AddSeconds(i),
                D = i,
                E = "hello",
                F = i * 2
            });
        }
        RunTest(foos, "Vanilla");
        Hyper.ComponentModel.HyperTypeDescriptionProvider.Add(
            typeof(MyData));
        RunTest(foos, "Hyper");
        Console.ReadLine(); // return to exit        
    }
}
Marc Gravell
sumber
4
Yah "sebagaimana adanya", itu akan menjadi secepat refleksi. Jika Anda mengaktifkan HyperDescriptor, itu akan meronta-ronta refleksi tangan ke bawah ... Saya akan menjalankan tes cepat ... (2 menit)
Marc Gravell
Ekspresi disebutkan untuk 3.5. Jika digunakan bagaimana pengaruhnya terhadap kode, apakah ada sampel?
MicMit
3
@MarcGravell Ya saya akan sangat tertarik dengan solusi Ekspresi. Untuk membutuhkan sesuatu yang cepat + efek belajar. Marc terima kasih!
Elisabeth
11
@ Ellesedil Saya berusaha keras untuk mengingat untuk secara eksplisit mengungkapkan hal-hal seperti itu, tetapi karena saya tidak menjual apa-apa (melainkan membuat banyak jam kerja tersedia secara bebas) Saya akui saya tidak merasakan banyak kesalahan di sini ...
Marc Gravell
2
metode Anda ToDataTable tidak mendukung bidang yang dapat dibatalkan: Informasi tambahan: DataSet tidak mendukung System.Nullable <>.
Dainius Kreivys
235

Saya harus memodifikasi kode sampel Marc Gravell untuk menangani tipe nullable dan nilai null. Saya telah memasukkan versi yang berfungsi di bawah ini. Terima kasih, Marc.

public static DataTable ToDataTable<T>(this IList<T> data)
{
    PropertyDescriptorCollection properties = 
        TypeDescriptor.GetProperties(typeof(T));
    DataTable table = new DataTable();
    foreach (PropertyDescriptor prop in properties)
        table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
    foreach (T item in data)
    {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
             row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        table.Rows.Add(row);
    }
    return table;
}
Mary Hamlin
sumber
Ini jawaban yang sangat bagus. Saya ingin melihat contoh ini diperluas untuk menangani grup dengan daftar yang akan berisi properti item dan memiliki kolom yang dibuat dengan cara yang sama di atas.
Coder Tidak Diketahui
2
Untuk mencapai @Jim Beam itu, ubah tanda tangan metode untuk menerima pengembalian GroupBy: public static DataTable ToDataTable<TKey, T>(this IEnumerable<IGrouping<TKey, T>> data) Lalu, tambahkan kolom tambahan sebelum loop foreach: table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); Dan kemudian tambahkan loop di sekitar loop data di mana Anda mengulangi grup: foreach (IGrouping <TKey, T> grup dalam data) {foreach (item T di group.Items) {Lihat GIST ini untuk detail lengkap: gist.github.com/rickdailey/8679306
Rick Dailey
hei, apakah ada cara untuk menangani objek dengan objek dalam? Saya hanya ingin Properti dalam muncul sebagai kolom setelah kolom objek induk
heyNow
@heyNow, saya yakin ada. Tapi saya tidak benar-benar membutuhkan fungsi itu dengan apa yang saya lakukan dan meninggalkannya untuk orang lain. :)
Mary Hamlin
1
Ini adalah posting lama jadi tidak yakin seberapa berguna komentar ini, tetapi ada satu bug yang licik dalam ToDataTablemetode ini . Jika Tmengimplementasikan antarmuka typeof(T)dapat mengembalikan jenis antarmuka daripada kelas objek yang sebenarnya, menghasilkan kosong DataTable. Menggantinya dengan data.First().GetType()harus memperbaikinya.
Lucas
14

Perubahan kecil pada jawaban Marc untuk membuatnya berfungsi dengan tipe nilai seperti List<string>tabel data:

public static DataTable ListToDataTable<T>(IList<T> data)
{
    DataTable table = new DataTable();

    //special handling for value types and string
    if (typeof(T).IsValueType || typeof(T).Equals(typeof(string)))
    {

        DataColumn dc = new DataColumn("Value");
        table.Columns.Add(dc);
        foreach (T item in data)
        {
            DataRow dr = table.NewRow();
            dr[0] = item;
            table.Rows.Add(dr);
        }
    }
    else
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
        foreach (PropertyDescriptor prop in properties)
        {
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        }
        foreach (T item in data)
        {
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
            {
                try
                {
                    row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                }
                catch (Exception ex)
                {
                    row[prop.Name] = DBNull.Value;
                }
            }
            table.Rows.Add(row);
        }
    }
    return table;
}
Onur Omer
sumber
Bagaimana cara membuatnya untuk Daftar <int>?
Muflix
1
Metode di atas akan bekerja untuk int (dan tipe nilai lainnya) juga ... int adalah tipe nilai. lihat: msdn.microsoft.com/en-us/library/s1ax56ch.aspx
Onur Omer
Saya suka ini karena tidak bergantung pada menggunakan metode ekstensi. Bekerja dengan baik untuk basis kode lama yang mungkin tidak memiliki akses ke Metode Ekstensi.
webworm
13

Ini adalah campuran sederhana dari solusi. Ini bekerja dengan tipe Nullable.

public static DataTable ToDataTable<T>(this IList<T> list)
{
  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, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
  }
  object[] values = new object[props.Count];
  foreach (T item in list)
  {
    for (int i = 0; i < values.Length; i++)
      values[i] = props[i].GetValue(item) ?? DBNull.Value;
    table.Rows.Add(values);
  }
  return table;
}
A.Baudouin
sumber
Solusi ini rawan kesalahan karena tergantung pada urutan deklarasi properti di kelas T.
Vahid Ghadiri
10

Tautan ini pada MSDN patut dikunjungi: Cara: Menerapkan CopyToDataTable <T> Di mana Generic Type T Bukan DataRow

Ini menambahkan metode ekstensi yang memungkinkan Anda melakukan ini:

// Create a sequence. 
Item[] items = new Item[] 
{ new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"}, 
  new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"},
  new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"},
  new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}};

// Query for items with price greater than 9.99.
var query = from i in items
             where i.Price > 9.99
             orderby i.Price
             select i;

// Load the query results into new DataTable.
DataTable table = query.CopyToDataTable();
Jürgen Steinblock
sumber
@ PaulWilliams Terima kasih, saya menggunakan kode ini selama bertahun-tahun tanpa masalah sejauh ini. Tetapi karena saya tidak menyalin kode contoh dari microsoft dan hanya menautkan ke situs web, solusi lain setidaknya lebih sesuai dengan jawaban praktik terbaik stackoverflow.com/help/how-to-answer
Jürgen Steinblock
8

Pendekatan lain adalah di atas:

  List<WhateEver> lst = getdata();
  string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst);
  DataTable pDt = JsonConvert.DeserializeObject<DataTable>(json);
kostas ch.
sumber
Sangat sangat bagus ... tapi itu membuang Exception dari tipe 'System.OutOfMemoryException'. Saya menggunakannya dengan 500 000 item ... Tapi terima kasih untuk ini.
st_stefanov
Sejauh ini, ini adalah solusi terbersih yang saya temukan di internet. Kerja bagus!
Sarah
7
public DataTable ConvertToDataTable<T>(IList<T> data)
{
    PropertyDescriptorCollection properties =
        TypeDescriptor.GetProperties(typeof(T));

    DataTable table = new DataTable();

    foreach (PropertyDescriptor prop in properties)
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);

    foreach (T item in data)
    {
        DataRow row = table.NewRow();
        foreach (PropertyDescriptor prop in properties)
        {
           row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
        }
        table.Rows.Add(row);
    }
    return table;
}
Boitumelo Dikoko
sumber
3
Sementara kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang mengapa dan / atau bagaimana kode ini menjawab pertanyaan meningkatkan nilai jangka panjangnya.
kayess
Solusi ini rawan kesalahan karena tergantung pada urutan deklarasi properti di kelas T.
Vahid Ghadiri
6

Jawaban Marc Gravell tetapi dalam VB.NET

Public Shared Function ToDataTable(Of T)(data As IList(Of T)) As DataTable
    Dim props As PropertyDescriptorCollection = TypeDescriptor.GetProperties(GetType(T))
    Dim table As New DataTable()
    For i As Integer = 0 To props.Count - 1
            Dim prop As PropertyDescriptor = props(i)
            table.Columns.Add(prop.Name, prop.PropertyType)
    Next
    Dim values As Object() = New Object(props.Count - 1) {}
    For Each item As T In data
            For i As Integer = 0 To values.Length - 1
                    values(i) = props(i).GetValue(item)
            Next
            table.Rows.Add(values)
    Next
    Return table
End Function
Craig Gjerdingen
sumber
6

coba ini

public static DataTable ListToDataTable<T>(IList<T> lst)
{

    currentDT = CreateTable<T>();

    Type entType = typeof(T);

    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
    foreach (T item in lst)
    {
        DataRow row = currentDT.NewRow();
        foreach (PropertyDescriptor prop in properties)
        {

            if (prop.PropertyType == typeof(Nullable<decimal>) || prop.PropertyType == typeof(Nullable<int>) || prop.PropertyType == typeof(Nullable<Int64>))
            {
                if (prop.GetValue(item) == null)
                    row[prop.Name] = 0;
                else
                    row[prop.Name] = prop.GetValue(item);
            }
            else
                row[prop.Name] = prop.GetValue(item);                    

        }
        currentDT.Rows.Add(row);
    }

    return currentDT;
}

public static DataTable CreateTable<T>()
{
    Type entType = typeof(T);
    DataTable tbl = new DataTable(DTName);
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
    foreach (PropertyDescriptor prop in properties)
    {
        if (prop.PropertyType == typeof(Nullable<decimal>))
             tbl.Columns.Add(prop.Name, typeof(decimal));
        else if (prop.PropertyType == typeof(Nullable<int>))
            tbl.Columns.Add(prop.Name, typeof(int));
        else if (prop.PropertyType == typeof(Nullable<Int64>))
            tbl.Columns.Add(prop.Name, typeof(Int64));
        else
             tbl.Columns.Add(prop.Name, prop.PropertyType);
    }
    return tbl;
}
Sadegh
sumber
6
It's also possible through XmlSerialization.
The idea is - serialize to `XML` and then `readXml` method of `DataSet`.

I use this code (from an answer in SO, forgot where)

        public static string SerializeXml<T>(T value) where T : class
    {
        if (value == null)
        {
            return null;
        }

        XmlSerializer serializer = new XmlSerializer(typeof(T));

        XmlWriterSettings settings = new XmlWriterSettings();

        settings.Encoding = new UnicodeEncoding(false, false);
        settings.Indent = false;
        settings.OmitXmlDeclaration = false;
        // no BOM in a .NET string

        using (StringWriter textWriter = new StringWriter())
        {
            using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings))
            {
               serializer.Serialize(xmlWriter, value);
            }
            return textWriter.ToString();
        }
    }

so then it's as simple as:

            string xmlString = Utility.SerializeXml(trans.InnerList);

        DataSet ds = new DataSet("New_DataSet");
        using (XmlReader reader = XmlReader.Create(new StringReader(xmlString)))
        { 
            ds.Locale = System.Threading.Thread.CurrentThread.CurrentCulture;
            ds.ReadXml(reader); 
        }

Not sure how it stands against all the other answers to this post, but it's also a possibility.
Mithir
sumber
5

Saya sendiri sudah menulis perpustakaan kecil untuk menyelesaikan tugas ini. Ini menggunakan refleksi hanya untuk pertama kalinya jenis objek harus diterjemahkan ke data. Itu memancarkan metode yang akan melakukan semua pekerjaan menerjemahkan jenis objek.

Sangat cepat. Anda dapat menemukannya di sini: ModelShredder di GoogleCode

Johannes Rudolph
sumber
2

Saya juga harus datang dengan solusi alternatif, karena tidak ada opsi yang tercantum di sini yang berfungsi dalam kasus saya. Saya menggunakan IEnumerable yang mengembalikan IEnumerable dan properti tidak dapat disebutkan. Ini berhasil:

// remove "this" if not on C# 3.0 / .NET 3.5
public static DataTable ConvertToDataTable<T>(this IEnumerable<T> data)
{
    List<IDataRecord> list = data.Cast<IDataRecord>().ToList();

    PropertyDescriptorCollection props = null;
    DataTable table = new DataTable();
    if (list != null && list.Count > 0)
    {
        props = TypeDescriptor.GetProperties(list[0]);
        for (int i = 0; i < props.Count; i++)
        {
            PropertyDescriptor prop = props[i];
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        }
    }
    if (props != null)
    {
        object[] values = new object[props.Count];
        foreach (T item in data)
        {
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = props[i].GetValue(item) ?? DBNull.Value;
            }
            table.Rows.Add(values);
        }
    }
    return table;
}
Michael Brown
sumber
2
  using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.ComponentModel;

public partial class Default3 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        DataTable dt = new DataTable();
        dt = lstEmployee.ConvertToDataTable();
    }
    public static DataTable ConvertToDataTable<T>(IList<T> list) where T : class
    {
        try
        {
            DataTable table = CreateDataTable<T>();
            Type objType = typeof(T);
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(objType);
            foreach (T item in list)
            {
                DataRow row = table.NewRow();
                foreach (PropertyDescriptor property in properties)
                {
                    if (!CanUseType(property.PropertyType)) continue;
                    row[property.Name] = property.GetValue(item) ?? DBNull.Value;
                }

                table.Rows.Add(row);
            }
            return table;
        }
        catch (DataException ex)
        {
            return null;
        }
        catch (Exception ex)
        {
            return null;
        }

    }
    private static DataTable CreateDataTable<T>() where T : class
    {
        Type objType = typeof(T);
        DataTable table = new DataTable(objType.Name);
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(objType);
        foreach (PropertyDescriptor property in properties)
        {
            Type propertyType = property.PropertyType;
            if (!CanUseType(propertyType)) continue;

            //nullables must use underlying types
            if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>))
                propertyType = Nullable.GetUnderlyingType(propertyType);
            //enums also need special treatment
            if (propertyType.IsEnum)
                propertyType = Enum.GetUnderlyingType(propertyType);
            table.Columns.Add(property.Name, propertyType);
        }
        return table;
    }


    private static bool CanUseType(Type propertyType)
    {
        //only strings and value types
        if (propertyType.IsArray) return false;
        if (!propertyType.IsValueType && propertyType != typeof(string)) return false;
        return true;
    }
}

sumber
2

Saya menyadari bahwa ini telah ditutup untuk sementara waktu; Namun, saya punya solusi untuk masalah khusus ini tetapi membutuhkan sedikit sentuhan: kolom dan tabel data harus ditentukan sebelumnya / sudah dipakai. Kemudian saya perlu memasukkan tipe-tipe ke tabel data.

Jadi, inilah contoh dari apa yang saya lakukan:

public static class Test
{
    public static void Main()
    {
        var dataTable = new System.Data.DataTable(Guid.NewGuid().ToString());

        var columnCode = new DataColumn("Code");
        var columnLength = new DataColumn("Length");
        var columnProduct = new DataColumn("Product");

        dataTable.Columns.AddRange(new DataColumn[]
            {
                columnCode,
                columnLength,
                columnProduct
            });

        var item = new List<SomeClass>();

        item.Select(data => new
        {
            data.Id,
            data.Name,
            data.SomeValue
        }).AddToDataTable(dataTable);
    }
}

static class Extensions
{
    public static void AddToDataTable<T>(this IEnumerable<T> enumerable, System.Data.DataTable table)
    {
        if (enumerable.FirstOrDefault() == null)
        {
            table.Rows.Add(new[] {string.Empty});
            return;
        }

        var properties = enumerable.FirstOrDefault().GetType().GetProperties();

        foreach (var item in enumerable)
        {
            var row = table.NewRow();
            foreach (var property in properties)
            {
                row[property.Name] = item.GetType().InvokeMember(property.Name, BindingFlags.GetProperty, null, item, null);
            }
            table.Rows.Add(row);
        }
    }
}
brenton
sumber
dapatkah Anda menunjukkan kepada saya dengan contoh. bagaimana saya menggunakan metode ekstensi untuk metode addtodataTable ()
Abhishek B.
Kode ini sudah memiliki contoh di dalamnya - lihat metode Main (). Bit kode terakhir memiliki ekstensi yang digunakan.
brenton
Untuk bacaan lebih lanjut, silakan lihat artikel ini dari MSDN tentang metode ekstensi: msdn.microsoft.com/en-us/library/bb383977.aspx
brenton
2

Jawaban 2019 jika Anda menggunakan .NET Core - gunakan pustaka Nuget ToDataTable . Keuntungan:

Penafian - Saya penulis ToDataTable

Kinerja - Saya merentang beberapa tes Benchmark .Net dan memasukkannya ke dalam repo ToDataTable . Hasilnya adalah sebagai berikut:

Membuat 100.000 Baris Datatable :

                           MacOS         Windows
Reflection                 818.5 ms      818.3 ms
FastMember from           1105.5 ms      976.4 ms
 Mark's answer
Improved FastMember        524.6 ms      456.4 ms
ToDataTable                449.0 ms      376.5 ms

Metode FastMember yang disarankan dalam jawaban Marc tampaknya berkinerja lebih buruk daripada jawaban Mary yang menggunakan refleksi, tetapi saya menggulirkan metode lain menggunakan FastMember TypeAccessordan kinerjanya jauh lebih baik. Namun demikian paket ToDataTable mengungguli banyak.

Chris HG
sumber
1

Jika Anda menggunakan VB.NET maka kelas ini melakukan pekerjaan.

Imports System.Reflection
''' <summary>
''' Convert any List(Of T) to a DataTable with correct column types and converts Nullable Type values to DBNull
''' </summary>

Public Class ConvertListToDataset

    Public Function ListToDataset(Of T)(ByVal list As IList(Of T)) As DataTable

        Dim dt As New DataTable()
        '/* Create the DataTable columns */
        For Each pi As PropertyInfo In GetType(T).GetProperties()
            If pi.PropertyType.IsValueType Then
                Debug.Print(pi.Name)
            End If
            If IsNothing(Nullable.GetUnderlyingType(pi.PropertyType)) Then
                dt.Columns.Add(pi.Name, pi.PropertyType)
            Else
                dt.Columns.Add(pi.Name, Nullable.GetUnderlyingType(pi.PropertyType))
            End If
        Next

        '/* Populate the DataTable with the values in the Items in List */
        For Each item As T In list
            Dim dr As DataRow = dt.NewRow()
            For Each pi As PropertyInfo In GetType(T).GetProperties()
                dr(pi.Name) = IIf(IsNothing(pi.GetValue(item)), DBNull.Value, pi.GetValue(item))
            Next
            dt.Rows.Add(dr)
        Next
        Return dt

    End Function

End Class
Jonathan Roberts
sumber
1

jika Anda memiliki properti di kelas Anda, baris kode ini OK !!

PropertyDescriptorCollection props =
            TypeDescriptor.GetProperties(typeof(T));

tetapi jika Anda memiliki semua bidang publik maka gunakan ini:

public static DataTable ToDataTable<T>(  IList<T> data)
        {
        FieldInfo[] myFieldInfo;
        Type myType = typeof(T);
        // Get the type and fields of FieldInfoClass.
        myFieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance
            | BindingFlags.Public);

        DataTable dt = new DataTable();
        for (int i = 0; i < myFieldInfo.Length; i++)
            {
            FieldInfo property = myFieldInfo[i];
            dt.Columns.Add(property.Name, property.FieldType);
            }
        object[] values = new object[myFieldInfo.Length];
        foreach (T item in data)
            {
            for (int i = 0; i < values.Length; i++)
                {
                values[i] = myFieldInfo[i].GetValue(item);
                }
            dt.Rows.Add(values);
            }
        return dt;
        }

jawaban asli dari atas, saya baru saja diedit untuk menggunakan bidang bukan properti

dan untuk menggunakannya lakukan ini

 DataTable dt = new DataTable();
            dt = ToDataTable(myBriefs);
            gridData.DataSource = dt;
            gridData.DataBind();
Masoud Cheragee
sumber
1

Untuk mengonversi daftar generik ke tabel data, Anda bisa menggunakan DataTableGenerator

Pustaka ini memungkinkan Anda mengubah daftar Anda menjadi tabel data dengan multi-fitur like

  • Terjemahkan tajuk tabel data
  • tentukan beberapa kolom untuk ditampilkan
Majid
sumber
1
  private DataTable CreateDataTable(IList<T> item)
        {
            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 item)
            {
                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;
        }
Maghalakshmi Saravana
sumber
1

Untuk Mengubah daftar Generik menjadi DataTable

menggunakan Newtonsoft.Json;

public DataTable GenericToDataTable(IList<T> list)
{
    var json = JsonConvert.SerializeObject(list);
    DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
    return dt;
}
Maghalakshmi Saravana
sumber
0

Ini adalah Aplikasi Konsol sederhana untuk mengonversi Daftar ke Datatable.

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

namespace ConvertListToDataTable
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            List<MyObject> list = new List<MyObject>();
            for (int i = 0; i < 5; i++)
            {
                list.Add(new MyObject { Sno = i, Name = i.ToString() + "-KarthiK", Dat = DateTime.Now.AddSeconds(i) });
            }

            DataTable dt = ConvertListToDataTable(list);
            foreach (DataRow row in dt.Rows)
            {
                Console.WriteLine();
                for (int x = 0; x < dt.Columns.Count; x++)
                {
                    Console.Write(row[x].ToString() + " ");
                }
            }
            Console.ReadLine();
        }

        public class MyObject
        {
            public int Sno { get; set; }
            public string Name { get; set; }
            public DateTime Dat { get; set; }
        }

        public static DataTable ConvertListToDataTable<T>(this List<T> iList)
        {
            DataTable dataTable = new DataTable();
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
            for (int i = 0; i < props.Count; i++)
            {
                PropertyDescriptor propertyDescriptor = props[i];
                Type type = propertyDescriptor.PropertyType;

                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                    type = Nullable.GetUnderlyingType(type);

                dataTable.Columns.Add(propertyDescriptor.Name, type);
            }
            object[] values = new object[props.Count];
            foreach (T iListItem in iList)
            {
                for (int i = 0; i < values.Length; i++)
                {
                    values[i] = props[i].GetValue(iListItem);
                }
                dataTable.Rows.Add(values);
            }
            return dataTable;
        }
    }
}
Karthikeyan P
sumber
0

Seolah Anda dapat memilih untuk menerima campo con null.

// remove "this" if not on C# 3.0 / .NET 3.5
    public static DataTable ToDataTable<T>(IList<T> data)
    {
        PropertyDescriptorCollection props =
            TypeDescriptor.GetProperties(typeof(T));
        DataTable table = new DataTable();
        Type Propiedad = null;
        for (int i = 0; i < props.Count; i++)
        {
            PropertyDescriptor prop = props[i];
            Propiedad = prop.PropertyType;
            if (Propiedad.IsGenericType && Propiedad.GetGenericTypeDefinition() == typeof(Nullable<>)) 
            {
                Propiedad = Nullable.GetUnderlyingType(Propiedad);
            }
            table.Columns.Add(prop.Name, Propiedad);
        }
        object[] values = new object[props.Count];
        foreach (T item in data)
        {
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = props[i].GetValue(item);
            }
            table.Rows.Add(values);
        }
        return table;
    }
Luis Rodrigo Carrasco Lagos
sumber
3
Selamat datang di Stack Overflow . Ini adalah situs web berbahasa Inggris, jadi harap tulis jawaban Anda dalam bahasa Inggris juga.
Peri
0
 Dim counties As New List(Of County)
 Dim dtCounties As DataTable
 dtCounties = _combinedRefRepository.Get_Counties()
 If dtCounties.Rows.Count <> 0 Then
    For Each row As DataRow In dtCounties.Rows
      Dim county As New County
      county.CountyId = row.Item(0).ToString()
      county.CountyName = row.Item(1).ToString().ToUpper()
      counties.Add(county)
    Next
    dtCounties.Dispose()
 End If
JoshYates1980
sumber
0

Saya pikir ini lebih nyaman dan mudah digunakan.

   List<Whatever> _lobj= new List<Whatever>(); 
    var json = JsonConvert.SerializeObject(_lobj);
                DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
Majedur Rahaman
sumber
0

Daftar / data = Daftar baru (); var dataDT = Newtonsoft.Json.JsonConvert.DeserializeObject (Newtonsoft.Json.JsonConvert.SerializeObject (data));

pengguna12815245
sumber
0

Jika Anda ingin menggunakan refleksi dan mengatur urutan kolom / hanya menyertakan beberapa kolom / Kecualikan beberapa kolom coba ini:

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