Convertire un elenco generico / enumerabile in DataTable?


261

Ho alcuni metodi che restituiscono diversi elenchi generici.

Esiste in .net un metodo statico di classe o qualsiasi altra cosa per convertire qualsiasi elenco in un datatable? L'unica cosa che posso immaginare è usare Reflection per farlo.

SE ho questo:

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

(Questo prossimo codice non funziona ovviamente, ma vorrei avere la possibilità di:

DataTable dt = (DataTable) whatever;

2
Certo, una buona domanda sarebbe "perché?" - quando Elenco <T> è in molti casi uno strumento migliore di DataTable ;-p Ciascuno per conto proprio, immagino ...
Marc Gravell

1
Penso che questo possa essere un duplicato di questa domanda: stackoverflow.com/questions/523153/… Ha persino una risposta quasi identica. :-)
mezoide,

2
@MarcGravell: Il mio "perché?" è manipolazione Elenco <T> (Attraversamento di colonne e righe). Sto provando a fare un perno da un Elenco <T> e accedere alle proprietà tramite la riflessione è un dolore. Sto sbagliando?
Eduardo Molteni,

1
@Eduardo ci sono un numero qualsiasi di strumenti per rimuovere il dolore riflesso lì - FastMember balza in mente. Essa potrebbe anche essere che un DataTable è utile per scenari specifici - tutto dipende dal contesto. Forse il problema più grande sono le persone che usano DataTable per tutto l'archiviazione dei dati solo perché esiste , senza prendere il tempo di considerare le opzioni e il loro scenario.
Marc Gravell

@EduardoMolteni se sei interessato, ho aggiornato FastMember per avere supporto diretto per questo - vedi la risposta aggiornata
Marc Gravell

Risposte:


325

Ecco un bel aggiornamento del 2013 con FastMember di NuGet:

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

Questo utilizza l'API di meta-programmazione di FastMember per le massime prestazioni. Se vuoi limitarlo a determinati membri (o applicare l'ordine), puoi farlo anche tu:

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

Dell'Editore Dis / reclamante: FastMember è un progetto di Marc Gravell. Il suo oro e le mosche piene!


Sì, questo è praticamente l'esatto contrario di questo ; la riflessione sarebbe sufficiente - o se hai bisogno di più veloce, HyperDescriptorin 2.0 o forse Expressionin 3.5. In realtà,HyperDescriptor dovrebbe essere più che adeguato.

Per esempio:

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

Ora con una riga puoi renderlo molte volte più veloce del riflesso (abilitando HyperDescriptoril tipo di oggetto T).


modifica query di performance; ecco un banco di prova con risultati:

Vanilla 27179
Hyper   6997

Sospetto che il collo di bottiglia sia passato dall'accesso dei membri a DataTable prestazioni ... Dubito che migliorerai molto su questo ...

codice:

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
Bene "così com'è", sarà veloce come la riflessione. Se abiliti HyperDescriptor, si romperà il riflesso verso il basso ... Eseguirò un test rapido ... (2 minuti)
Marc Gravell

L'espressione è stata menzionata per 3.5. Se utilizzato, come influirebbe sul codice, c'è qualche esempio?
MicMit

3
@MarcGravell Sì, sarei molto interessato alla soluzione Expression. Per aver bisogno di qualcosa di veloce + effetto di apprendimento. Grazie Marc!
Elisabeth,

11
@Ellesedil Mi sforzo di ricordare di rivelare esplicitamente tali cose, ma dal momento che non sto vendendo nulla (ma piuttosto sto facendo molte ore di lavoro liberamente disponibili), confesso che qui non provo grandi sensi di colpa ...
Marc Gravell

2
il tuo metodo ToDataTable non supporta i campi null: Informazioni aggiuntive: DataSet non supporta System.Nullable <>.
Dainius Kreivys,

235

Ho dovuto modificare il codice di esempio di Marc Gravell per gestire tipi nullable e valori null. Ho incluso una versione funzionante di seguito. Grazie 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;
}

Questa è una risposta eccellente Mi piacerebbe vedere questo esempio ampliato per gestire un gruppo in base all'elenco che conterrebbe una proprietà dell'articolo e che le colonne vengano create nello stesso modo sopra.
Unknown Coder

2
Per ottenere quel @Jim Beam, modifica la firma del metodo per accettare il ritorno di GroupBy: public static DataTable ToDataTable<TKey, T>(this IEnumerable<IGrouping<TKey, T>> data) Quindi, aggiungi una colonna aggiuntiva prima del ciclo foreach: table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); E quindi aggiungi un ciclo attorno al ciclo dati in cui esegui l'iterazione dei gruppi: foreach (IGrouping <TKey, T> group in data) {foreach (oggetto T in group.Items) {Vedi questo GIST per tutti i dettagli: gist.github.com/rickdailey/8679306
Rick Dailey,

ehi, c'è un modo per gestire un oggetto con oggetti interni? Voglio solo che le Proprietà interne appaiano come colonne dopo le colonne dell'oggetto genitore
heyNow,

@heyNow, sono sicuro che ci sia. Ma non avevo davvero bisogno di quella funzionalità con quello che stavo facendo e quindi l'ho lasciato per qualcun altro da estendere. :)
Mary Hamlin,

1
Questo è un vecchio post, quindi non sono sicuro di quanto sia utile questo commento, ma c'è un bug subdolo in questo ToDataTablemetodo. Se Timplementa un'interfaccia typeof(T)potrebbe restituire il tipo di interfaccia anziché la classe effettiva dell'oggetto, risultando in un vuoto DataTable. Sostituendolo con data.First().GetType()dovrebbe risolverlo.
Lucas,

14

Una piccola modifica alla risposta di Marc per farla funzionare con tipi di valore come List<string>nella tabella dei dati:

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

Come farlo per List <int>?
Muflix,

1
Il metodo sopra funzionerà anche per int (e altri tipi di valore) ... int è un tipo di valore. vedi: msdn.microsoft.com/en-us/library/s1ax56ch.aspx
Onur Omer

Mi piace perché non dipende dall'uso di un metodo di estensione. Funziona bene con basi di codice meno recenti che potrebbero non avere accesso ai metodi di estensione.
webworm,

13

Questo è un semplice mix di soluzioni. Funziona con tipi 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;
}

Questa soluzione è soggetta a errori poiché dipende dall'ordine di dichiarazione delle proprietà nella classe T.
Vahid Ghadiri,

10

Questo collegamento su MSDN merita una visita: Procedura: implementare CopyToDataTable <T> dove il tipo T generico non è un DataRow

Questo aggiunge un metodo di estensione che ti consente di fare questo:

// 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 Grazie, uso questo codice da anni senza problemi finora. Ma dato che non ho copiato il codice di esempio di Microsoft e legata solo al sito web le altre soluzioni sono almeno più compatibile con la risposta migliori pratiche stackoverflow.com/help/how-to-answer~~V~~plural~~3rd
Jürgen Steinblock

8

Un altro approccio è il precedente:

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

Molto molto bene ... ma ha generato un'eccezione di tipo "System.OutOfMemoryException". L'ho usato con 500.000 articoli ... Ma grazie per questo.
st_stefanov,

Questa è di gran lunga la soluzione più pulita che ho trovato in rete. Ottimo lavoro!
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;
}

3
Mentre questo codice può rispondere alla domanda, fornendo un contesto aggiuntivo riguardo al perché e / o al modo in cui questo codice risponde alla domanda migliora il suo valore a lungo termine.
kayess,

Questa soluzione è soggetta a errori poiché dipende dall'ordine di dichiarazione delle proprietà nella classe T.
Vahid Ghadiri,

6

La risposta di Marc Gravell ma in 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

prova questo

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

Ho scritto una piccola biblioteca per svolgere questo compito. Usa la riflessione solo per la prima volta che un tipo di oggetto deve essere tradotto in un datatable. Emette un metodo che farà tutto il lavoro traducendo un tipo di oggetto.

È velocissimo. Puoi trovarlo qui: ModelShredder su GoogleCode


2

Ho anche dovuto trovare una soluzione alternativa, poiché nessuna delle opzioni elencate qui ha funzionato nel mio caso. Stavo usando un IEnumerable che ha restituito un IEnumerable e le proprietà non potevano essere enumerate. Questo ha fatto il trucco:

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

Mi rendo conto che questo è stato chiuso per un po '; tuttavia, avevo una soluzione a questo problema specifico, ma avevo bisogno di una leggera svolta: le colonne e la tabella dei dati dovevano essere predefiniti / già istanziati. Quindi ho dovuto semplicemente inserire i tipi nella tabella dei dati.

Quindi, ecco un esempio di quello che ho fatto:

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

puoi mostrarmi con l'esempio. come uso il metodo di estensione per i metodi addtodataTable ()
Abhishek B.

Questo codice contiene già un esempio: dai un'occhiata al metodo Main (). L'ultimo bit di codice ha l'estensione utilizzata.
Brenton,

Per ulteriori approfondimenti, si prega di guardare a questo articolo da MSDN sui metodi di estensione: msdn.microsoft.com/en-us/library/bb383977.aspx
Brenton

2

Una risposta per il 2019 se si utilizza .NET Core: utilizzare la libreria Nuget ToDataTable . vantaggi:

Disclaimer - Sono l'autore di ToDataTable

Prestazioni : ho superato alcuni test Benchmark .Net e li ho inclusi nel repository ToDataTable . I risultati sono stati i seguenti:

Creazione di un datatable di 100.000 righe :

                           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

Il metodo FastMember suggerito nella risposta di Marc sembrava peggiorare della risposta di Mary che utilizzava la riflessione, ma ho implementato un altro metodo usando un FastMember TypeAccessore ha funzionato molto meglio. Tuttavia, il pacchetto ToDataTable ha superato il lotto.


1

Se si utilizza VB.NET, questa classe fa il lavoro.

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

se hai proprietà nella tua classe questa riga di codice è OK !!

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

ma se hai tutti i campi pubblici, usa questo:

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

la risposta originale è dall'alto, ho appena modificato per utilizzare i campi anziché le proprietà

e per usarlo fai questo

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

1

Per convertire un elenco generico in tabella di dati, è possibile utilizzare DataTableGenerator

Questa libreria ti consente di convertire il tuo elenco in una tabella di dati con like multi-funzionalità

  • Traduci l'intestazione della tabella dati
  • specifica alcune colonne da mostrare

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

Per convertire un elenco generico in DataTable

utilizzando Newtonsoft.Json;

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

0

Questa è la semplice applicazione console per convertire l'elenco in 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

Probabilmente il metodo per accedere a campi 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;
    }

3
Benvenuto in Stack Overflow . Questo è un sito web di lingua inglese, quindi scrivi anche le tue risposte in inglese.
Fata

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

Penso che sia più conveniente e facile da usare.

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

0

Elenco / dati = nuovo elenco (); var dataDT = Newtonsoft.Json.JsonConvert.DeserializeObject (Newtonsoft.Json.JsonConvert.SerializeObject (data));



0

Se vuoi usare la riflessione e impostare l'ordine delle colonne / includere solo alcune colonne / Escludere alcune colonne prova questo:

        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++;
        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.