Czy konwertować ogólną listę / listę do DataTable?

261

Mam kilka metod, które zwracają różne listy ogólne.

Istnieje w .net jakakolwiek metoda statyczna klasy lub cokolwiek, aby przekonwertować dowolną listę na dane? Jedyne, co mogę sobie wyobrazić, to użyć Reflection, aby to zrobić.

JEŚLI mam to:

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

(Ten następny kod oczywiście nie działa, ale chciałbym mieć możliwość:

DataTable dt = (DataTable) whatever;
Josema
źródło
2
Oczywiście dobrym pytaniem byłoby „dlaczego?” - kiedy Lista <T> jest w wielu przypadkach lepszym narzędziem niż DataTable ;-p Wydaje mi się, że każdy z nich ...
Marc Gravell
1
Myślę, że to może być duplikat tego pytania: stackoverflow.com/questions/523153/... Ma nawet prawie identyczną odpowiedź. :-)
mezoid
2
@MarcGravell: Moje „dlaczego?” to manipulacja List <T> (przemierzanie kolumn i wierszy). Próbuję zrobić punkt obrotu z Listy <T> i dostęp do właściwości poprzez refleksję to ból. Robię to źle?
Eduardo Molteni,
1
@Eduardo jest wiele narzędzi do usuwania bólu odbijającego - FastMember przychodzi mi do głowy. Może być również tak, że DataTable jest przydatna w określonych scenariuszach - wszystko zależy od kontekstu. Być może największym problemem są ludzie używający DataTable do przechowywania wszystkich danych tylko dlatego, że istnieje , bez poświęcania czasu na rozważenie opcji i ich scenariusza.
Marc Gravell
@EduardoMolteni, jeśli jesteś zainteresowany, zaktualizowałem FastMember, aby uzyskać bezpośrednie wsparcie w tym zakresie - zobacz zaktualizowaną odpowiedź
Marc Gravell

Odpowiedzi:

325

Oto ładna aktualizacja 2013 za pomocą FastMember od NuGet:

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

Używa meta-programowania API FastMember dla maksymalnej wydajności. Jeśli chcesz ograniczyć go do określonych członków (lub egzekwować zamówienie), możesz to również zrobić:

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

Redaktor Dis / pretendent: FastMember to projekt Marc Gravell. Złoto i pełne muchy!


Tak, jest to dokładne przeciwieństwo tego ; wystarczy refleksja - lub jeśli potrzebujesz szybciej, HyperDescriptorw wersji 2.0 lub Expression3.5. W rzeczywistości HyperDescriptorpowinno być więcej niż wystarczające.

Na przykład:

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

Teraz za pomocą jednej linii możesz zrobić to wiele razy szybciej niż odbicie (poprzez włączenie HyperDescriptortypu obiektu T).


edytuj zapytanie dotyczące wydajności; oto platforma testowa z wynikami:

Vanilla 27179
Hyper   6997

Podejrzewam, że wąskie gardło zmieniło się z dostępu członków na DataTablewydajność ... Wątpię, czy znacznie poprawisz to ...

kod:

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
źródło
4
Cóż „tak jak jest”, będzie tak samo szybkie jak odbicie. Jeśli włączysz HyperDescriptor, spowoduje to rzucenie refleksji w dół ... Przeprowadzę szybki test ... (2 minuty)
Marc Gravell
Wyrażenie wspomniano w 3.5. Jeśli użyty, jak wpłynie to na kod, czy jest jakaś próbka?
MicMit
3
@MarcGravell Tak Byłbym bardzo zainteresowany rozwiązaniem Expression. Za potrzebę czegoś szybkiego + efekt uczenia się. Dziękuję Marc!
Elisabeth
11
@Ellesedil Staram się pamiętać o jawnym ujawnieniu takich rzeczy, ale ponieważ nic nie sprzedaję (ale raczej udostępniam wiele godzin pracy za darmo), przyznaję, że nie odczuwam tutaj ogromnej winy ...
Marc Gravell
2
twoja metoda ToDataTable nie obsługuje pól zerowalnych: Informacje dodatkowe: DataSet nie obsługuje System.Nullable <>.
Dainius Kreivys
235

Musiałem zmodyfikować przykładowy kod Marc Gravell, aby obsługiwał typy zerowalne i wartości zerowe. Poniżej zamieściłem działającą wersję. Dzięki 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
źródło
To doskonała odpowiedź. Chciałbym zobaczyć, jak ten przykład został rozszerzony, aby obsługiwać grupę według listy, która zawierałaby właściwość item i miała kolumny utworzone w taki sam sposób powyżej.
Nieznany Coder
2
Aby osiągnąć @Jim Beam, zmień podpis metody, aby zaakceptować zwrot GroupBy: public static DataTable ToDataTable<TKey, T>(this IEnumerable<IGrouping<TKey, T>> data) Następnie dodaj dodatkową kolumnę przed pętlą foreach: table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); A następnie dodaj pętlę wokół pętli danych, w której iterujesz grupy: foreach (IGrouping <TKey, T> grupa w danych) {foreach (element T w grupie. Elementy) {Aby uzyskać szczegółowe informacje, zobacz GIST: gist.github.com/rickdailey/8679306
Rick Dailey
hej, czy istnieje sposób na obsługę obiektu z obiektami wewnętrznymi? Chcę tylko, aby wewnętrzne Właściwości pojawiły się jako kolumny po kolumnach obiektu nadrzędnego
hejNow
@ heyNow, jestem pewien, że jest. Ale tak naprawdę nie potrzebowałem tej funkcjonalności z tym, co robiłem, więc zostawiłem ją dla kogoś innego. :)
Mary Hamlin
1
To jest stary post, więc nie jestem pewien, jak przydatny jest ten komentarz, ale w tej ToDataTablemetodzie jest jeden podstępny błąd . Jeśli Timplementuje interfejs, typeof(T)może zwrócić typ interfejsu zamiast faktycznej klasy obiektu, co spowoduje, że będzie pusty DataTable. Zastąpienie go data.First().GetType()powinno to naprawić.
Lucas
14

Mała zmiana w odpowiedzi Marca, aby działała z typami wartości, takimi jak List<string>tabela danych:

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
źródło
Jak zrobić to dla Listy <int>?
Muflix,
1
Powyższa metoda będzie działać również dla int (i innych typów wartości) ... int jest typem wartości. patrz: msdn.microsoft.com/en-us/library/s1ax56ch.aspx
Onur Omer
Podoba mi się to, ponieważ nie zależy to od zastosowania metody rozszerzenia. Działa dobrze w przypadku starszych baz kodu, które mogą nie mieć dostępu do metod rozszerzeń.
webworm
13

To prosta kombinacja rozwiązań. Działa z typami dopuszczającymi wartości zerowe.

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
źródło
To rozwiązanie jest podatne na błędy, ponieważ zależy od kolejności deklaracji właściwości w klasie T.
Vahid Ghadiri
10

Ten link w witrynie MSDN jest warty odwiedzenia: Jak: Implementować CopyToDataTable <T> Gdzie ogólny typ T nie jest DataRow

Dodaje to metodę rozszerzenia, która pozwala to zrobić:

// 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
źródło
@PaulWilliams Dzięki, używam tego kodu przez lata bez problemu do tej pory. Ale ponieważ nie skopiowałem przykładowego kodu z Microsoft i tylko łączyłem się ze stroną internetową, inne rozwiązania są co najmniej bardziej zgodne z najlepszymi praktykami stackoverflow.com/help/how-to-answer
Jürgen Steinblock
8

Innym podejściem jest powyższe:

  List<WhateEver> lst = getdata();
  string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst);
  DataTable pDt = JsonConvert.DeserializeObject<DataTable>(json);
kostas ch.
źródło
Bardzo, bardzo dobrze ... ale wygenerował wyjątek typu „System.OutOfMemoryException”. Użyłem go z 500 000 przedmiotów ... Ale dziękuję za to.
st_stefanov,
To zdecydowanie najczystsze rozwiązanie, jakie znalazłem w sieci. Świetna robota!
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
źródło
3
Chociaż ten kod może odpowiedzieć na pytanie, zapewnienie dodatkowego kontekstu dotyczącego tego, dlaczego i / lub jak ten kod odpowiada na pytanie, poprawia jego długoterminową wartość.
kayess,
To rozwiązanie jest podatne na błędy, ponieważ zależy od kolejności deklaracji właściwości w klasie T.
Vahid Ghadiri
6

Odpowiedź Marca Gravella, ale w 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
źródło
6

Spróbuj tego

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
źródło
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
źródło
5

Sam napisałem małą bibliotekę, aby wykonać to zadanie. Wykorzystuje odbicie tylko po raz pierwszy, gdy typ obiektu ma zostać przetłumaczony na dane. Emituje metodę, która wykona całą pracę tłumacząc typ obiektu.

Płonie szybko. Można go znaleźć tutaj: ModelShredder na GoogleCode

Johannes Rudolph
źródło
2

Musiałem też wymyślić alternatywne rozwiązanie, ponieważ żadna z wymienionych tutaj opcji nie działała w moim przypadku. Używałem IEnumerable, który zwrócił IEnumerable, a właściwości nie można wyliczyć. To załatwiło sprawę:

// 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
źródło
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;
    }
}

źródło
2

Zdaję sobie sprawę, że zostało to na jakiś czas zamknięte; Miałem jednak rozwiązanie tego konkretnego problemu, ale potrzebowałem lekkiego skrętu: kolumny i tabela danych musiały być predefiniowane / już utworzone. Następnie musiałem po prostu wstawić typy do tabeli danych.

Oto przykład tego, co zrobiłem:

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
źródło
możesz mi pokazać z przykładem? jak używam metody rozszerzenia dla metod addtodataTable ()
Abhishek B.
Ten kod zawiera już przykład - spójrz na metodę Main (). Ostatni bit kodu ma używane rozszerzenie.
brenton,
Więcej informacji można znaleźć w tym artykule MSDN na temat metod rozszerzenia: msdn.microsoft.com/en-us/library/bb383977.aspx
brenton
2

Odpowiedź na 2019 rok, jeśli korzystasz z .NET Core - skorzystaj z biblioteki ToDataTable firmy Nuget . Zalety:

Oświadczenie - Jestem autorem ToDataTable

Wydajność - rozszerzyłem niektóre testy Benchmark .Net i umieściłem je w repozytorium ToDataTable . Wyniki były następujące:

Tworzenie 100 000 wierszy danych :

                           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

Metoda FastMember sugerowana w odpowiedzi Marca wydawała się działać gorzej niż odpowiedź Mary, która używała refleksji, ale rzuciłem inną metodę za pomocą FastMember TypeAccessori działała znacznie lepiej. Niemniej jednak pakiet ToDataTable wypadł znacznie lepiej.

Chris HG
źródło
1

Jeśli używasz VB.NET, ta klasa wykonuje to zadanie.

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
źródło
1

jeśli masz właściwości w swojej klasie, ten wiersz kodu jest OK !!

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

ale jeśli masz wszystkie pola publiczne, użyj tego:

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

oryginalna odpowiedź jest z góry, właśnie edytowałem, aby użyć pól zamiast właściwości

i aby z niego skorzystać, zrób to

 DataTable dt = new DataTable();
            dt = ToDataTable(myBriefs);
            gridData.DataSource = dt;
            gridData.DataBind();
masoud Cheragee
źródło
1

Aby przekonwertować ogólną listę na tabelę danych, możesz użyć DataTableGenerator

Ta biblioteka pozwala przekonwertować listę na tabelę danych zawierającą wiele funkcji

  • Przetłumacz nagłówek tabeli danych
  • określ kolumnę do wyświetlenia
Majid
źródło
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
źródło
1

Aby przekonwertować listę ogólną na tabelę danych

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
źródło
0

Jest to prosta aplikacja konsoli do konwersji listy na dane.

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.
źródło
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
źródło
2
Witamy w Stack Overflow . To jest strona anglojęzyczna, więc proszę również pisać odpowiedzi po angielsku.
Wróżka
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
źródło
0

Myślę, że jest wygodniejszy i łatwiejszy w użyciu.

   List<Whatever> _lobj= new List<Whatever>(); 
    var json = JsonConvert.SerializeObject(_lobj);
                DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
Majedur Rahaman
źródło
0

List / data = new List (); var dataDT = Newtonsoft.Json.JsonConvert.DeserializeObject (Newtonsoft.Json.JsonConvert.SerializeObject (dane));

użytkownik12815245
źródło
0

Jeśli chcesz użyć odbicia i ustawić kolejność kolumn / uwzględnij tylko niektóre kolumny / Wyklucz niektóre kolumny, spróbuj:

        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
źródło