Converter lista / enumerável genérica em DataTable?

261

Eu tenho alguns métodos que retornam diferentes listas genéricas.

Existe no .net algum método estático de classe ou qualquer outra coisa para converter qualquer lista em uma tabela de dados? A única coisa que posso imaginar é usar o Reflection para fazer isso.

Se eu tenho isso:

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

(Este próximo código não funciona, é claro, mas eu gostaria de ter a possibilidade de:

DataTable dt = (DataTable) whatever;
Josema
fonte
2
Obviamente, uma boa pergunta seria "por que?" - quando List <T> é, em muitos casos, uma ferramenta melhor que DataTable ;-p Cada um por conta própria, eu acho ...
Marc Gravell
1
Eu acho que este pode ser um duplicado desta pergunta: stackoverflow.com/questions/523153/… Ele ainda tem uma resposta quase idêntica. :-)
mezoid
2
@MarcGravell: Meu "por quê?" é manipulação de Lista <T> (Atravessando colunas e linhas). Estou tentando fazer um pivô a partir de uma lista <T> e acessar as propriedades via reflexão é uma dor. Estou fazendo errado?
Eduardo Molteni
1
@ Eduardo, existem inúmeras ferramentas para remover a dor da reflexão - o FastMember vem à mente. Também pode ser que uma DataTable seja útil para cenários específicos - tudo depende do contexto. Talvez o maior problema seja as pessoas que usam o DataTable para todo o armazenamento de dados apenas porque ele existe , sem gastar tempo para considerar as opções e seu cenário.
Marc Gravell
@EduardoMolteni, se você estiver interessado, atualizei o FastMember para ter suporte direto para isso - veja a resposta atualizada
Marc Gravell

Respostas:

325

Aqui está uma boa atualização de 2013 usando o FastMember do NuGet:

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

Isso usa a API de metaprogramação do FastMember para obter o máximo desempenho. Se você deseja restringi-lo a membros específicos (ou aplicar o pedido), também pode fazer isso:

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

Do editor Dis / reclamante: FastMember é um projecto Marc Gravell. Seu ouro e moscas cheias!


Sim, esse é exatamente o oposto deste ; reflexão seria suficiente - ou se você precisar mais rápido, HyperDescriptorem 2.0 ou talvez Expressionem 3.5. Na verdade, HyperDescriptordeve ser mais do que adequado.

Por exemplo:

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

Agora, com uma linha, você pode fazer isso muitas vezes mais rápido que a reflexão (ativando HyperDescriptoro tipo de objeto T).


editar consulta de desempenho re; aqui está uma plataforma de teste com resultados:

Vanilla 27179
Hyper   6997

Eu suspeito que o gargalo mudou do acesso dos membros ao DataTabledesempenho ... duvido que você melhore muito nisso ...

código:

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
fonte
4
Bem "como está", será tão rápido quanto a reflexão. Se você ativar HyperDescriptor, ele vai thrash reflexão mãos para baixo ... Eu vou executar um teste rápido ... (2 minutos)
Marc Gravell
A expressão foi mencionada em 3,5. Se usado, como isso afetaria o código, existe algum exemplo?
MicMit 30/03/10
3
@ MarcGravell Sim, eu ficaria muito interessado na solução Expression. Por precisar de algo rápido + efeito de aprendizado. Obrigado Marc!
Elisabeth #
11
@ Ellesedil Tento me lembrar de divulgar explicitamente essas coisas, mas como não estou vendendo nada (mas estou disponibilizando muitas horas de trabalho gratuitamente), confesso que não sinto muita culpa aqui ...
Marc Gravell
2
seu método ToDataTable não oferece suporte a campos anuláveis: Informações adicionais: O DataSet não oferece suporte a System.Nullable <>.
Dainius Kreivys 27/10/2015
235

Eu tive que modificar o código de exemplo de Marc Gravell para lidar com tipos e valores nulos. Eu incluí uma versão de trabalho abaixo. Obrigado 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
fonte
Esta é uma excelente resposta. Eu adoraria ver esse exemplo expandido para lidar com um grupo por lista que conteria uma propriedade de item e teria colunas criadas da mesma maneira acima.
Unknown Coder
2
Para obter esse @ Jim Beam, altere a assinatura do método para aceitar o retorno de GroupBy: public static DataTable ToDataTable<TKey, T>(this IEnumerable<IGrouping<TKey, T>> data) Em seguida, adicione uma coluna extra antes do loop foreach: table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); E adicione um loop ao redor do loop de dados onde você itera os grupos: foreach (IGrouping <TKey, T> grupo de dados) {foreach (item de T em group.Items) {Veja este GIST para mais detalhes: gist.github.com/rickdailey/8679306
Rick Dailey
Ei, existe uma maneira de lidar com um objeto com objetos internos? Eu só quero que as propriedades internas para aparecer como colunas após colunas do objeto pai
Heynow
@heyNow, tenho certeza de que existe. Mas eu realmente não precisava dessa funcionalidade com o que estava fazendo e, portanto, deixei para outra pessoa estender. :)
Mary Hamlin
1
Este é um post antigo, portanto, não sei o quão útil é esse comentário, mas há um bug no ToDataTablemétodo. Se Timplementa uma interface, typeof(T)pode retornar o tipo de interface em vez da classe real do objeto, resultando em um vazio DataTable. Substituí-lo por data.First().GetType()deve corrigi-lo.
Lucas
14

Uma pequena alteração na resposta de Marc para fazê-lo funcionar com tipos de valor como List<string>a tabela de dados:

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
fonte
Como fazê-lo para a Lista <int>?
Muflix 01/09/16
1
O método acima também funcionará para int (e outros tipos de valor) ... int é um tipo de valor. consulte: msdn.microsoft.com/en-us/library/s1ax56ch.aspx
Onur Omer
Eu gosto disso porque não depende do uso de um método de extensão. Funciona bem para bases de código antigas que podem não ter acesso aos métodos de extensão.
Webworm
13

Esta é uma mistura simples de soluções. Funciona com tipos anuláveis.

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
fonte
Essa solução é suscetível a erros porque depende da ordem das declarações de propriedades na classe T.
Vahid Ghadiri
10

Vale a pena visitar este link no MSDN: Como: Implementar CopyToDataTable <T> Onde o tipo genérico T não é um DataRow

Isso adiciona um método de extensão que permite fazer isso:

// 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
fonte
@PaulWilliams Obrigado, eu uso esse código há anos sem problemas até agora. Mas como não copiei o código de exemplo da microsoft e vinculei apenas ao site, as outras soluções são pelo menos mais compatíveis com as práticas recomendadas da resposta stackoverflow.com/help/how-to-answer
Jürgen Steinblock
8

Outra abordagem é a acima:

  List<WhateEver> lst = getdata();
  string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst);
  DataTable pDt = JsonConvert.DeserializeObject<DataTable>(json);
kostas ch.
fonte
Muito, muito bom ... mas lançou Exception do tipo 'System.OutOfMemoryException'. Eu usei com 500 000 itens ... Mas obrigado por isso.
22418 st_stefanov
Esta é de longe a solução mais limpa que encontrei na rede. Ótimo trabalho!
Sarah 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
fonte
3
Embora esse código possa responder à pergunta, fornecer um contexto adicional a respeito de por que e / ou como esse código responde à pergunta melhora seu valor a longo prazo.
Kayess
Essa solução é suscetível a erros porque depende da ordem das declarações de propriedades na classe T.
Vahid Ghadiri
6

A resposta de Marc Gravell, mas no 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
fonte
6

tente isso

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
fonte
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
fonte
5

Eu mesmo escrevi uma pequena biblioteca para realizar essa tarefa. Ele usa reflexão apenas pela primeira vez que um tipo de objeto deve ser convertido em uma tabela de dados. Emite um método que fará todo o trabalho de conversão de um tipo de objeto.

É muito rápido. Você pode encontrá-lo aqui: ModelShredder no GoogleCode

Johannes Rudolph
fonte
2

Eu também tive que encontrar uma solução alternativa, pois nenhuma das opções listadas aqui funcionava no meu caso. Eu estava usando um IEnumerable que retornou um IEnumerable e as propriedades não puderam ser enumeradas. Isso fez o truque:

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

fonte
2

Eu percebo que isso foi fechado por um tempo; no entanto, eu tinha uma solução para esse problema específico, mas precisava de uma ligeira reviravolta: as colunas e a tabela de dados precisavam ser predefinidas / já instanciadas. Então eu precisava simplesmente inserir os tipos na tabela de dados.

Então, aqui está um exemplo do que eu fiz:

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
fonte
você pode me mostrar com exemplo? como eu uso método de extensão por addtodataTable () métodos
Abhishek B.
Este código já possui um exemplo - dê uma olhada no método Main (). O último bit de código tem a extensão sendo usada.
Brenton
Para uma leitura mais detalhada, consulte este artigo do MSDN sobre métodos de extensão: msdn.microsoft.com/en-us/library/bb383977.aspx
brenton
2

Uma resposta de 2019 se você estiver usando o .NET Core - use a biblioteca Nuget ToDataTable . Vantagens:

Isenção de responsabilidade - eu sou o autor de ToDataTable

Desempenho - eu incluo alguns testes de benchmark .Net e os incluí no repositório ToDataTable . Os resultados foram os seguintes:

Criando uma tabela de dados de 100.000 linhas :

                           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

O método FastMember sugerido na resposta de Marc parecia ter um desempenho pior do que a resposta de Mary, que usava reflexão, mas eu rolei outro método usando um FastMember TypeAccessore ele teve um desempenho muito melhor. No entanto, o pacote ToDataTable superou o desempenho.

Chris HG
fonte
1

Se você estiver usando o VB.NET, essa classe fará o trabalho.

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
fonte
1

se você tiver propriedades em sua classe, esta linha de código está OK !!

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

mas se você tiver todos os campos públicos, use este:

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

a resposta original é de cima, eu apenas editei para usar campos em vez de propriedades

e para usá-lo faça isso

 DataTable dt = new DataTable();
            dt = ToDataTable(myBriefs);
            gridData.DataSource = dt;
            gridData.DataBind();
masoud Cheragee
fonte
1

Para converter uma lista genérica em tabela de dados, você pode usar o DataTableGenerator

Essa biblioteca permite converter sua lista em uma tabela de dados com vários recursos, como

  • Traduzir cabeçalho da tabela de dados
  • especifique alguma coluna para mostrar
Majid
fonte
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
fonte
1

Para converter lista genérica em DataTable

using 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
fonte
0

Este é o aplicativo simples do console para converter List em 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
fonte
0

Se você usar o método para aceitar campos com nulo.

// 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
fonte
3
Bem-vindo ao Stack Overflow . Este é um site de língua inglesa, portanto, escreva suas respostas também em inglês.
10287 Fairy
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
fonte
0

Eu acho que é mais conveniente e fácil de usar.

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

Lista / dados = nova Lista (); var dataDT = Newtonsoft.Json.JsonConvert.DeserializeObject (Newtonsoft.Json.JsonConvert.SerializeObject (data));

user12815245
fonte
0

Se você deseja usar a reflexão e definir a ordem das colunas / incluir apenas algumas colunas / Excluir algumas colunas, tente o seguinte:

        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
fonte