Generische Liste / Aufzählung in DataTable konvertieren?

261

Ich habe nur wenige Methoden, die unterschiedliche generische Listen zurückgeben.

Gibt es in .net eine statische Klassenmethode oder was auch immer, um eine Liste in eine Datentabelle umzuwandeln? Das einzige, was ich mir vorstellen kann, ist Reflection, um dies zu tun.

WENN ich das habe:

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

(Dieser nächste Code funktioniert natürlich nicht, aber ich hätte gerne die Möglichkeit:

DataTable dt = (DataTable) whatever;
Josema
quelle
2
Eine gute Frage wäre natürlich "warum?" - wenn List <T> in vielen Fällen ein besseres Werkzeug als DataTable ist ;-p Jeder für sich, denke ich ...
Marc Gravell
1
Ich denke, dies könnte ein Duplikat dieser Frage sein: stackoverflow.com/questions/523153/… Es hat sogar eine nahezu identische Antwort. :-)
Mezoid
2
@MarcGravell: Mein "Warum?" ist List <T> -Manipulation (Durchlaufen von Spalten und Zeilen). Ich versuche, aus einer Liste <T> einen Pivot zu machen, und der Zugriff auf die Eigenschaften über Reflexion ist ein Schmerz. Ich mache es falsch
Eduardo Molteni
1
@Eduardo Es gibt eine beliebige Anzahl von Werkzeugen, um den Reflexionsschmerz dort zu beseitigen - FastMember fällt mir ein. Es kann auch sein, dass eine DataTable für bestimmte Szenarien nützlich ist - alles hängt vom Kontext ab. Das vielleicht größte Problem besteht darin, dass Benutzer DataTable für die gesamte Datenspeicherung verwenden, nur weil es vorhanden ist , ohne sich die Zeit zu nehmen, die Optionen und ihr Szenario zu berücksichtigen.
Marc Gravell
@EduardoMolteni Wenn Sie interessiert sind, habe ich FastMember aktualisiert, um direkte Unterstützung dafür zu haben - siehe die aktualisierte Antwort
Marc Gravell

Antworten:

325

Hier ist ein schönes 2013-Update mit FastMember von NuGet:

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

Dies verwendet die Meta-Programmier-API von FastMember für maximale Leistung. Wenn Sie es auf bestimmte Mitglieder beschränken möchten (oder die Anordnung durchsetzen möchten), können Sie dies auch tun:

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

Dis / Claimer des Herausgebers : FastMember ist ein Marc Gravell-Projekt. Sein Gold und seine Fliegen!


Ja, das ist so ziemlich das genaue Gegenteil von diesem ; Reflexion würde ausreichen - oder wenn Sie schneller brauchen, HyperDescriptorin 2.0 oder vielleicht Expressionin 3.5. Eigentlich HyperDescriptorsollte mehr als ausreichend sein.

Beispielsweise:

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

Mit einer Zeile können Sie dies jetzt um ein Vielfaches schneller als die Reflexion machen (indem Sie HyperDescriptorden Objekttyp aktivieren T).


Bearbeiten Sie die Leistungsabfrage. Hier ist ein Prüfstand mit Ergebnissen:

Vanilla 27179
Hyper   6997

Ich vermute, dass sich der Engpass vom Mitgliederzugriff auf die DataTableLeistung verlagert hat ... Ich bezweifle, dass Sie dies erheblich verbessern werden ...

Code:

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
quelle
4
Nun, "wie es ist", es wird ungefähr so ​​schnell wie das Nachdenken sein. Wenn Sie HyperDescriptor aktivieren, wird es die Reflexion zweifellos verprügeln ... Ich werde einen kurzen Test durchführen ... (2 Minuten)
Marc Gravell
Die Expression wurde für 3.5 erwähnt. Wenn es verwendet wird, wie würde es den Code beeinflussen, gibt es ein Beispiel?
MicMit
3
@MarcGravell Ja, die Expression-Lösung würde mich sehr interessieren. Für etwas schnelles + Lerneffekt. Danke Marc!
Elisabeth
11
@Ellesedil Ich bemühe mich, mich daran zu erinnern, solche Dinge explizit offenzulegen, aber da ich nichts verkaufe (sondern viele Stunden Arbeit frei zur Verfügung
stelle
2
Ihre Methode ToDataTable unterstützt keine nullbaren Felder: Zusätzliche Informationen: DataSet unterstützt System.Nullable <> nicht.
Dainius Kreivys
235

Ich musste den Beispielcode von Marc Gravell ändern, um nullfähige Typen und null Werte zu verarbeiten. Ich habe unten eine Arbeitsversion eingefügt. Danke 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
quelle
Dies ist eine ausgezeichnete Antwort. Ich würde gerne sehen, wie dieses Beispiel erweitert wird, um eine Gruppe nach Liste zu behandeln, die eine Elementeigenschaft enthält und Spalten auf die gleiche Weise wie oben erstellt hat.
Unbekannter Codierer
2
Um dies zu erreichen, ändern Sie die Methodensignatur, um die Rückgabe von GroupBy zu akzeptieren: Fügen Sie public static DataTable ToDataTable<TKey, T>(this IEnumerable<IGrouping<TKey, T>> data) dann eine zusätzliche Spalte vor der foreach-Schleife hinzu: table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); Fügen Sie dann eine Schleife um die Datenschleife hinzu, in der Sie die Gruppen iterieren: foreach (IGrouping <TKey, T> Gruppe in Daten) {foreach (T-Element in Gruppe.Items) {Siehe diese GIST für vollständige Details: gist.github.com/rickdailey/8679306
Rick Dailey
Hey, gibt es eine Möglichkeit, ein Objekt mit inneren Objekten zu behandeln? Ich möchte nur, dass die inneren Eigenschaften als Spalten nach den Spalten des übergeordneten Objekts
angezeigt werden
@heyNow, ich bin sicher, dass es gibt. Aber ich brauchte diese Funktionalität bei dem, was ich tat, nicht wirklich und überließ es daher jemand anderem, sie zu erweitern. :)
Mary Hamlin
1
Dies ist ein alter Beitrag, daher nicht sicher, wie nützlich dieser Kommentar ist, aber es gibt einen hinterhältigen Fehler in dieser ToDataTableMethode. Wenn Teine Schnittstelle implementiert wird, wird typeof(T)möglicherweise der Schnittstellentyp anstelle der tatsächlichen Klasse des Objekts zurückgegeben, was zu einem Leerzeichen führt DataTable. Das Ersetzen durch data.First().GetType()sollte das Problem beheben.
Lucas
14

Eine kleine Änderung an Marc's Antwort , damit sie mit Werttypen wie List<string>der Datentabelle funktioniert :

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
quelle
Wie mache ich es für List <int>?
Muflix
1
Die obige Methode funktioniert auch für int (und andere Werttypen) ... int ist ein Werttyp. siehe: msdn.microsoft.com/en-us/library/s1ax56ch.aspx
Onur Omer
Ich mag das, weil es nicht von der Verwendung einer Erweiterungsmethode abhängt. Funktioniert gut für ältere Codebasen, die möglicherweise keinen Zugriff auf Erweiterungsmethoden haben.
Webworm
13

Dies ist eine einfache Mischung der Lösungen. Es funktioniert mit nullbaren Typen.

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
quelle
Diese Lösung ist fehleranfällig, da sie von der Reihenfolge der Eigenschaftsdeklaration in der T-Klasse abhängt.
Vahid Ghadiri
10

Dieser Link auf MSDN ist einen Besuch wert : Gewusst wie: Implementieren von CopyToDataTable <T> Wenn der generische Typ T keine DataRow ist

Dies fügt eine Erweiterungsmethode hinzu, mit der Sie Folgendes tun können:

// 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
quelle
@PaulWilliams Danke, ich benutze diesen Code seit Jahren ohne Probleme. Da ich den Beispielcode jedoch nicht von Microsoft kopiert und nur mit der Website verlinkt habe, entsprechen die anderen Lösungen zumindest besser den Best Practices für Antworten. Stackoverflow.com/help/how-to-answer
Jürgen Steinblock
8

Ein anderer Ansatz ist der oben genannte:

  List<WhateEver> lst = getdata();
  string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst);
  DataTable pDt = JsonConvert.DeserializeObject<DataTable>(json);
kostas ch.
quelle
Sehr sehr gut ... aber es warf eine Ausnahme vom Typ 'System.OutOfMemoryException'. Ich habe es mit 500 000 Artikeln verwendet ... Aber danke dafür.
st_stefanov
Dies ist bei weitem die sauberste Lösung, die ich im Internet gefunden habe. Gute Arbeit!
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
quelle
3
Während dieser Code die Frage möglicherweise beantwortet, verbessert die Bereitstellung eines zusätzlichen Kontexts darüber, warum und / oder wie dieser Code die Frage beantwortet, ihren langfristigen Wert.
Kayess
Diese Lösung ist fehleranfällig, da sie von der Reihenfolge der Eigenschaftsdeklaration in der T-Klasse abhängt.
Vahid Ghadiri
6

Marc Gravells Antwort aber 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
Craig Gjerdingen
quelle
6

Versuche dies

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

Ich habe selbst eine kleine Bibliothek geschrieben, um diese Aufgabe zu erfüllen. Die Reflexion wird nur zum ersten Mal verwendet, wenn ein Objekttyp in eine Datentabelle übersetzt werden soll. Es gibt eine Methode aus, die die gesamte Arbeit zur Übersetzung eines Objekttyps erledigt.

Es ist blitzschnell. Sie finden es hier: ModelShredder auf GoogleCode

Johannes Rudolph
quelle
2

Ich musste auch eine alternative Lösung finden, da in meinem Fall keine der hier aufgeführten Optionen funktionierte. Ich habe eine IEnumerable verwendet, die eine IEnumerable zurückgegeben hat, und die Eigenschaften konnten nicht aufgelistet werden. Das hat den Trick gemacht:

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

quelle
2

Mir ist klar, dass dies für eine Weile geschlossen ist; Ich hatte jedoch eine Lösung für dieses spezielle Problem, brauchte aber eine leichte Wendung: Die Spalten und die Datentabelle mussten vordefiniert / bereits instanziiert sein. Dann musste ich einfach die Typen in die Datentabelle einfügen.

Hier ist ein Beispiel für das, was ich getan habe:

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
quelle
Kannst du mir ein Beispiel zeigen? wie ich die Erweiterungsmethode für addtodataTable () -Methoden verwende
Abhishek B.
Dieser Code enthält bereits ein Beispiel - sehen Sie sich die Main () -Methode an. Das letzte Codebit hat die verwendete Erweiterung.
Brenton
Weitere Informationen finden Sie in diesem Artikel von MSDN zu Erweiterungsmethoden: msdn.microsoft.com/en-us/library/bb383977.aspx
brenton
2

Eine Antwort für 2019, wenn Sie .NET Core verwenden - verwenden Sie die Nuget ToDataTable-Bibliothek . Vorteile:

Haftungsausschluss - Ich bin der Autor von ToDataTable

Leistung - Ich habe einige Benchmark .Net- Tests zusammengefasst und sie in das ToDataTable-Repo aufgenommen . Die Ergebnisse waren wie folgt:

Erstellen einer Datentabelle mit 100.000 Zeilen :

                           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

Die in Marc's Antwort vorgeschlagene FastMember-Methode schien schlechter zu funktionieren als Marys Antwort, die Reflexion verwendete, aber ich habe eine andere Methode mit einem FastMember gewürfelt TypeAccessorund sie lief viel besser. Trotzdem übertraf das ToDataTable- Paket das Los.

Chris HG
quelle
1

Wenn Sie VB.NET verwenden, erledigt diese Klasse den Job.

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

Wenn Sie Eigenschaften in Ihrer Klasse haben, ist diese Codezeile in Ordnung !!

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

Wenn Sie jedoch alle öffentlichen Felder haben, verwenden Sie Folgendes:

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

Die ursprüngliche Antwort stammt von oben. Ich habe sie nur bearbeitet, um Felder anstelle von Eigenschaften zu verwenden

und um es zu benutzen, mach das

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

Um eine generische Liste in eine Datentabelle zu konvertieren, können Sie den DataTableGenerator verwenden

Mit dieser Bibliothek können Sie Ihre Liste in eine Datentabelle mit mehreren Funktionen wie konvertieren

  • Datentabellenkopf übersetzen
  • Geben Sie eine Spalte an, die angezeigt werden soll
Majid
quelle
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
quelle
1

So konvertieren Sie die generische Liste in DataTable

mit 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
quelle
0

Dies ist die einfache Konsolenanwendung zum Konvertieren von List 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;
        }
    }
}
Karthikeyan P.
quelle
0

Se probó el método para que acepte campos con null.

// remove "this" if not on C# 3.0 / .NET 3.5
    public static DataTable ToDataTable<T>(IList<T> data)
    {
        PropertyDescriptorCollection props =
            TypeDescriptor.GetProperties(typeof(T));
        DataTable table = new DataTable();
        Type Propiedad = null;
        for (int i = 0; i < props.Count; i++)
        {
            PropertyDescriptor prop = props[i];
            Propiedad = prop.PropertyType;
            if (Propiedad.IsGenericType && Propiedad.GetGenericTypeDefinition() == typeof(Nullable<>)) 
            {
                Propiedad = Nullable.GetUnderlyingType(Propiedad);
            }
            table.Columns.Add(prop.Name, Propiedad);
        }
        object[] values = new object[props.Count];
        foreach (T item in data)
        {
            for (int i = 0; i < values.Length; i++)
            {
                values[i] = props[i].GetValue(item);
            }
            table.Rows.Add(values);
        }
        return table;
    }
Luis Rodrigo Carrasco Lagos
quelle
2
Willkommen bei Stack Overflow . Dies ist eine englischsprachige Website. Bitte schreiben Sie Ihre Antworten auch auf Englisch.
Fee
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
quelle
0

Ich denke, es ist bequemer und einfacher zu bedienen.

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

Liste / Daten = neue Liste (); var dataDT = Newtonsoft.Json.JsonConvert.DeserializeObject (Newtonsoft.Json.JsonConvert.SerializeObject (Daten));

user12815245
quelle
0

Wenn Sie Reflektion verwenden und die Spaltenreihenfolge festlegen möchten / nur einige Spalten einschließen / einige Spalten ausschließen möchten, versuchen Sie Folgendes:

        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
quelle