Преобразовать общий список / перечисляемый в DataTable?

261

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

Существует ли в .net какой-либо статический метод класса или любой другой способ преобразования любого списка в таблицу данных? Единственное, что я могу себе представить, это использовать Reflection для этого.

Если у меня есть это:

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

(Следующий код, конечно, не работает, но я хотел бы иметь возможность:

DataTable dt = (DataTable) whatever;
Josema
источник
2
Конечно, хороший вопрос будет "почему?" - когда List <T> во многих случаях является лучшим инструментом, чем DataTable ;-p, я думаю, каждый из них сам по себе ...
Марк Грэвелл
1
Я думаю, что этот может быть дубликатом этого вопроса: stackoverflow.com/questions/523153/… У него даже есть почти идентичный ответ. :-)
мезоид
2
@MarcGravell: мое "почему?" является манипулированием списком <T> (обход столбцов и строк). Я пытаюсь сделать свод из List <T> и доступ к свойствам с помощью рефлексии, это боль. Я делаю это неправильно?
Эдуардо Молтени
1
@ Eduardo, существует множество инструментов для устранения боли при отражении - FastMember приходит на ум. Это может быть также , что DataTable полезно конкретных сценариев - все это зависит от контекста. Возможно, самая большая проблема заключается в том, что люди используют DataTable для всего хранилища данных только потому, что оно существует , не тратя время на рассмотрение вариантов и их сценария.
Марк Гравелл
@EduardoMolteni, если вам интересно, я обновил FastMember, чтобы иметь прямую поддержку для этого - см. Обновленный ответ
Марк Гравелл

Ответы:

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 / claimer редактора : FastMember - это проект Марка Гравелла. Его золото и полные мухи!


Да, это в значительной степени полная противоположность этому ; отражения будет достаточно - или, если вам нужно быстрее, 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

Мне пришлось изменить пример кода Марка Гравелла, чтобы обрабатывать обнуляемые типы и значения NULL. Я включил рабочую версию ниже. Спасибо Марк.

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) {Подробную информацию см. В этом GIST: gist.github.com/rickdailey/8679306
Рик Дейли,
эй, есть ли способ обработать объект с внутренними объектами? Я просто хочу, чтобы внутренние свойства отображались в виде столбцов после столбцов родительского объекта
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;
}
Онур Омер
источник
Как сделать это для List <int>?
Muflix
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;
}
A.Baudouin
источник
Это решение подвержено ошибкам, поскольку оно зависит от порядка объявления свойств в классе T.
Вахид Гадири
10

Эту ссылку на MSDN стоит посетить: Как: реализовать CopyToDataTable <T>, где универсальный тип T не является строкой данных

Это добавляет метод расширения, который позволяет вам сделать это:

// 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
Юрген Стейнблок
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
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, повышает его долгосрочную ценность.
kayess
Это решение подвержено ошибкам, поскольку оно зависит от порядка объявления свойств в классе 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;
}
Sadegh
источник
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
источник
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 ()
Абхишек Б.
В этом коде уже есть пример - взгляните на метод Main (). Последний бит кода имеет используемое расширение.
Брентон
Для дальнейшего чтения, пожалуйста, посмотрите эту статью из MSDN о методах расширения: msdn.microsoft.com/en-us/library/bb383977.aspx
Брентон
2

Ответ 2019, если вы используете .NET Core - используйте библиотеку Nuget ToDataTable . Преимущества:

  • Лучшая производительность, чем FastMember
  • Также создает структурированные SqlParameters для использования в качестве табличных параметров SQL Server

Отказ от ответственности - я автор 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

С этой точки зрения вы не найдете ничего другого.

// 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;
    }
Луис Родриго Карраско Лагос
источник
3
Добро пожаловать в переполнение стека . Это англоязычный веб-сайт, поэтому, пожалуйста, напишите ответы на английском языке.
Фея
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
источник
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 (data));

user12815245
источник
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++;
        }
    }
Ahmed_mag
источник