Перетворити загальний список / Численні в DataTable?


261

У мене мало методів, які повертають різні загальні списки.

Існує в .net будь-який статичний метод класу або що-небудь для перетворення будь-якого списку в таблицю даних? Єдине, що я можу собі уявити, це використовувати Reflection для цього.

ЯКЩО я маю це:

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

(Цей наступний код, звичайно, не працює, але я хотів би мати можливість:

DataTable dt = (DataTable) whatever;

2
Звичайно, хорошим питанням було б "чому?" - коли Список <T> у багатьох випадках є кращим інструментом, ніж DataTable ;-p Кожен по-своєму, я думаю ...
Марк Гравелл

1
Я думаю, що це може бути дублікатом цього питання: stackoverflow.com/questions/523153/… На нього навіть є майже однакова відповідь. :-)
мезоїд

2
@MarcGravell: Моє "чому?" це список <T> маніпулювання (проходження стовпців та рядків). Я намагаюся зробити поворот із списку <T> та отримати доступ до властивостей за допомогою рефлексії - це біль. Я роблю це неправильно?
Едуардо Молтені

1
@Eduardo є будь-яка кількість інструментів для зняття болю при відбитті - FastMember стрибає на розум. Це може бути також , що DataTable корисно конкретних сценаріїв - все це залежить від контексту. Мабуть, найбільша проблема - це люди, які використовують DataTable для всього зберігання даних лише тому, що він існує , не витрачаючи часу на розгляд варіантів та їх сценарію.
Марк Гравелл

@EduardoMolteni якщо вас цікавить, я оновив FastMember, щоб мати пряму підтримку для цього - дивіться оновлену відповідь
Marc Gravell

Відповіді:


325

Ось приємне оновлення 2013 року за допомогою FastMember від NuGet:

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

Для максимальної продуктивності використовується API метапрограмування FastMember. Якщо ви хочете обмежити це певними учасниками (або примусити розпорядження), тоді ви можете зробити це також:

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

Редактора Dis / стягувач: FastMember проект Марк Gravell. Його золото і повноцінні мухи!


Так, це майже навпаки цьому ; рефлексії буде достатньо - або якщо вам потрібно швидше, HyperDescriptorв 2.0 або, можливо, Expressionв 3.5. Насправді HyperDescriptorмає бути більш ніж адекватним.

Наприклад:

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

Тепер за допомогою одного рядка ви можете зробити це у багато разів швидше, ніж відображення (включивши HyperDescriptorтип об’єкта T).


редагувати запит на ефективність; ось тестова установка з результатами:

Vanilla 27179
Hyper   6997

Я підозрюю, що вузьке місце перейшло від доступу членів до DataTableпродуктивності ... я сумніваюся, що ви багато в чому поліпшите ...

код:

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

4
Ну "як є", це буде приблизно так само швидко, як і рефлексія. Якщо ви ввімкнете HyperDescriptor, він зруйнує роздуми руками ... Я проведу швидкий тест ... (2 хвилини)
Марк Гравелл

Вираз згадувався для 3,5. Як використовується, як це вплине на код, чи є зразок?
MicMit

3
@MarcGravell Так, я б дуже зацікавився рішенням Expression. Для необхідності чогось швидкого + ефекту навчання. Дякую Марку!
Елізабет

11
@Ellesedil Я дуже намагаюся пам'ятати, щоб явно розкривати такі речі, але оскільки я нічого не продаю (а радше роблю багато годин роботи вільно доступними), зізнаюся, я не відчуваю тут великої кількості вини ...
Марк Гравелл

2
ваш метод ToDataTable не підтримує зведені поля: Додаткова інформація: DataSet не підтримує System.Nullable <>.
Дайній Крейвіс

235

Мені довелося змінити зразок коду Марка Гравелла для обробки нульових типів і нульових значень. Я включив робочу версію нижче. Дякую Марку.

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

Це відмінна відповідь. Мені б хотілося, щоб цей приклад розгорнувся для обробки групи за списком, яка містила б властивість елемента та стовпці, створені таким же чином вище.
Невідомий Кодер

2
Щоб досягти цього @Jim Beam, змініть підпис методу, щоб прийняти повернення GroupBy: public static DataTable ToDataTable<TKey, T>(this IEnumerable<IGrouping<TKey, T>> data) Потім додайте додатковий стовпець перед циклом foreach: table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); а потім додайте цикл навколо циклу даних, куди ви ітератуєте групи: foreach (IGrouping <TKey, T> група в даних) {foreach (T елемент у group.Items) {Детальну інформацію див. У цьому ГІСТЕ
Rick Dailey

ей, чи існує спосіб поводження з предметом із внутрішніми предметами? Я просто хочу, щоб внутрішні властивості відображалися як стовпці після стовпців батьківського об’єкта
heyNow

@heyNow, я впевнений, що є. Але в мене справді не було потреби в цій функціональності з тим, що я роблю, і тому я залишив її для розширення комусь іншого. :)
Мері Хамлін

1
Це стара публікація, тому не впевнений, наскільки корисний цей коментар, але в цьому ToDataTableметоді є одна хитра помилка . Якщо Tреалізується інтерфейс, typeof(T)може повернути тип інтерфейсу, а не фактичний клас об'єкта, в результаті чого він буде порожнім DataTable. Замінивши його, data.First().GetType()слід виправити.
Лукас

14

Невелика зміна відповіді Марка, щоб вона працювала з типами значень, як List<string>таблиця даних:

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

Як зробити це для Список <int>?
Муфлікс

1
Наведений вище метод також буде працювати для int (та інших типів значень) ... int є типом значення. дивіться: msdn.microsoft.com/en-us/library/s1ax56ch.aspx
Онур Омер

Мені це подобається, оскільки це не залежить від використання методу розширення. Добре працює для старих баз кодів, які можуть не мати доступу до методів розширення.
черв'як

13

Це проста суміш розчинів. Він працює з типами 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;
}

Це рішення схильне до помилок, оскільки воно залежить від порядку декларування властивостей у T класі.
Вахід Гадірі

10

Це посилання на MSDN варто відвідати: як: впровадити CopyToDataTable <T> там, де загальний тип T не є DataRow

Це додає метод розширення, який дозволяє вам це робити:

// 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();

@PaulWilliams Дякую, я використовую цей код протягом багатьох років без проблем. Але так як я не копіював приклад коду з Microsoft і пов'язаний тільки з веб - сайту на інші рішення , по крайней мере , поступливішим з відповіддю кращі практики stackoverflow.com/help/how-to-answer
Юргена Steinblock

8

Ще один підхід:

  List<WhateEver> lst = getdata();
  string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst);
  DataTable pDt = JsonConvert.DeserializeObject<DataTable>(json);

Дуже добре ... але він кинув виняток типу "System.OutOfMemoryException". Я використав його з 500 000 предметів ... Але дякую за це.
st_stefanov

Це, безумовно, найчистіше рішення, яке я знайшов у мережі. Чудова робота!
Сара

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

3
Хоча цей код може відповісти на питання, надаючи додатковий контекст щодо того, чому та / або як цей код відповідає на питання, покращує його довгострокове значення.
кайєс

Це рішення схильне до помилок, оскільки воно залежить від порядку декларування властивостей у T класі.
Вахід Гадірі

6

Відповідь Марка Гравелла, але у 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

6

спробуйте це

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

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.

5

Я сам написав невелику бібліотеку для виконання цього завдання. Він використовує відображення лише вперше, коли тип об'єкта повинен бути переведений у таблицю даних. Він випромінює метод, який виконає всю роботу з перекладу типу об’єкта.

Її палає швидко. Ви можете знайти його тут: ModelShredder в GoogleCode


2

Я також повинен був придумати альтернативне рішення, оскільки жоден із перелічених тут варіантів не працював у моєму випадку. Я використовував IEnumerable, який повертав IEnumerable і властивості неможливо було перерахувати. Це зробило трюк:

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

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

2

Я розумію, що це було закрито на деякий час; однак я вирішив цю конкретну проблему, але потребував невеликого повороту: стовпці та таблицю даних потрібно було заздалегідь задати / вже створити. Тоді мені потрібно було просто вставити типи в таблицю даних.

Ось ось приклад того, що я зробив:

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

ви можете показати мені на прикладі. як я використовую метод розширення для методів addtodataTable ()
Abhishek B.

Цей код уже має приклад - подивіться на метод Main (). Останній біт коду використовує розширення.
Брентон

Для подальшого читання перегляньте цю статтю з MSDN про методи розширення: msdn.microsoft.com/en-us/library/bb383977.aspx
brenton

2

Відповідь 2019 року, якщо ви використовуєте .NET Core - використовуйте бібліотеку Nuget ToDataTable . Переваги:

Відмова - Я автор ToDataTable

Продуктивність - я охоплював деякі тести Benchmark .Net і включав їх до репортажу ToDataTable . Результати були такими:

Створення даних про 100 000 рядків :

                           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

Метод FastMember, запропонований у відповіді Марка, виглядав гірше, ніж відповідь Марії, яка використовувала рефлексію, але я прокатував інший метод, використовуючи FastMember, TypeAccessorі він працював набагато краще. Тим не менш, пакет ToDataTable перевершив партію.


1

Якщо ви використовуєте VB.NET, цей клас виконує цю роботу.

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

1

якщо у вас є властивості у вашому класі, цей рядок коду нормально !!

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

але якщо у вас є всі публічні поля, використовуйте це:

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

оригінальна відповідь зверху, я щойно редагував, щоб використовувати поля замість властивостей

і використовувати це робити це

 DataTable dt = new DataTable();
            dt = ToDataTable(myBriefs);
            gridData.DataSource = dt;
            gridData.DataBind();

1

Для перетворення загального списку в таблицю даних ви можете використовувати DataTableGenerator

Ця бібліотека дозволяє вам перетворити свій список у таблицю даних з такою багатофункціональністю

  • Перекласти заголовок таблиці даних
  • вкажіть деякий стовпчик для показу

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

1

Перетворення загального списку в DataTable

за допомогою Newtonsoft.Json;

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

0

Це проста консольна програма для перетворення списку в 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;
        }
    }
}

0

Se probó el método para que acepte campos 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;
    }

2
Ласкаво просимо до переповнення стека . Це англомовний веб-сайт, тому, будь ласка, напишіть свої відповіді також англійською мовою.
Фея

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

0

Я думаю, що це зручніше і простіше у використанні.

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

0

Список / дані = новий Список (); var dataDT = Newtonsoft.Json.JsonConvert.DeserializeObject (Newtonsoft.Json.JsonConvert.SerializeObject (дані));



0

Якщо ви хочете використовувати відображення та встановити порядок стовпців / включити лише деякі стовпці / Виключити деякі стовпці, спробуйте це:

        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++;
        }
    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.