Convertir la liste générique / énumérable en DataTable?


261

I have few methods that returns different Generic Lists.

Exists in .net any class static method or whatever to convert any list into a datatable? The only thing that i can imagine is use Reflection to do this.

IF i have this:

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

(This next code doesn't work of course, but i would like to have the possibility of:

DataTable dt = (DataTable) whatever;

2
Of course, a good question would be "why?" - when List<T> is in many cases a better tool than DataTable ;-p Each to their own, I guess...
Marc Gravell

1
I think this one may be a duplicate of this question: stackoverflow.com/questions/523153/… It even has a near identical answer. :-)
mezoid

2
@MarcGravell: My "why?" is List<T> manipulation (Traversing columns & rows). I'm trying to make a pivot from a List<T> and accessing the properties via reflexion it's a pain. I'm doing it wrong?
Eduardo Molteni

1
@Eduardo there are any number of tools to remove the reflection pain there - FastMember leaps to mind. It could also be that a DataTable is useful to specific scenarios - it all depends on the context. Perhaps the biggest problem is people using DataTable for all data storage just because it exists, without taking the time to consider the options and their scenario.
Marc Gravell

@EduardoMolteni if you are interested, I updated FastMember to have direct support for this - see the updated answer
Marc Gravell

Réponses:


325

Here's a nice 2013 update using FastMember from NuGet:

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

This uses the FastMember's meta-programming API for maximum performance. If you want to restrict it to particular members (or enforce the order), then you can do that too:

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

Editor's Dis/claimer: FastMember is a Marc Gravell project. Its gold and full-on flys!


Oui, c'est à peu près exactement le contraire de celui- ci; la réflexion suffirait - ou si vous avez besoin de plus rapide, HyperDescriptoren 2.0, ou peut-être Expressionen 3.5. En fait, HyperDescriptordevrait être plus que suffisant.

Par exemple:

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

Maintenant, avec une seule ligne, vous pouvez rendre cela beaucoup plus rapide que la réflexion (en activant HyperDescriptorpour le type d'objet T).


modifier re requête de performance; voici un banc d'essai avec des résultats:

Vanilla 27179
Hyper   6997

Je soupçonne que le goulot d'étranglement est passé de l'accès des membres à la DataTableperformance ... Je doute que vous amélioriez beaucoup cela ...

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

4
Eh bien "tel quel", ce sera à peu près aussi rapide que la réflexion. Si vous activez HyperDescriptor, il lancera la réflexion de haut en bas ... Je ferai un test rapide ... (2 minutes)
Marc Gravell

L'expression a été mentionnée pour 3.5. Si utilisé, comment cela affecterait-il le code, y a-t-il un échantillon?
MicMit

3
@MarcGravell Oui, je serais très intéressé par la solution Expression. Pour avoir besoin de quelque chose de rapide + effet d'apprentissage. Merci Marc!
Elisabeth

11
@Ellesedil Je m'efforce de ne pas oublier de divulguer explicitement de telles choses, mais comme je ne vends rien (mais plutôt que je mets gratuitement de nombreuses heures de travail), j'avoue que je ne ressens pas d'énormes quantités de culpabilité ici ...
Marc Gravell

2
votre méthode ToDataTable ne prend pas en charge les champs nullables: Informations supplémentaires: DataSet ne prend pas en charge System.Nullable <>.
Dainius Kreivys

235

J'ai dû modifier l'exemple de code de Marc Gravell pour gérer les types et les valeurs Nullables. J'ai inclus une version de travail ci-dessous. Merci 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;
}

Ceci est une excellente réponse. J'aimerais voir cet exemple développé pour gérer un groupe par liste qui contiendrait une propriété d'élément et aurait des colonnes créées de la même manière ci-dessus.
Unknown Coder

2
Pour y parvenir @Jim Beam, modifiez la signature de la méthode pour accepter le retour de GroupBy: public static DataTable ToDataTable<TKey, T>(this IEnumerable<IGrouping<TKey, T>> data) Ensuite, ajoutez une colonne supplémentaire avant la boucle foreach: table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); Et puis ajoutez une boucle autour de la boucle de données où vous itérez les groupes: foreach (IGrouping <TKey, T> groupe dans les données) {foreach (élément T dans group.Items) {Voir cet GIST pour plus de détails: gist.github.com/rickdailey/8679306
Rick Dailey

Hé, existe-t-il un moyen de gérer un objet avec des objets intérieurs? Je veux juste que les propriétés internes apparaissent sous forme de colonnes après les colonnes de l'objet parent
heyNow

@heyNow, je suis sûr qu'il y en a. Mais je n'avais pas vraiment besoin de cette fonctionnalité avec ce que je faisais et je l'ai donc laissé à quelqu'un d'autre d'étendre. :)
Mary Hamlin

1
Il s'agit d'un ancien article, donc vous ne savez pas à quel point ce commentaire est utile, mais il y a un bogue sournois dans cette ToDataTableméthode. Si Timplémente une interface typeof(T)peut renvoyer le type d'interface plutôt que la classe réelle de l'objet, résultant en un vide DataTable. Le remplacer par data.First().GetType()devrait le réparer.
Lucas

14

Un petit changement à la réponse de Marc pour le faire fonctionner avec des types de valeurs comme List<string>dans le tableau de données:

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

Comment faire pour List <int>?
Muflix

1
La méthode ci-dessus fonctionnera également pour int (et d'autres types de valeur) ... int est un type de valeur. voir: msdn.microsoft.com/en-us/library/s1ax56ch.aspx
Onur Omer

J'aime cela car cela ne dépend pas de l'utilisation d'une méthode d'extension. Fonctionne bien pour les anciennes bases de code qui peuvent ne pas avoir accès aux méthodes d'extension.
webworm

13

Il s'agit d'un simple mélange de solutions. Cela fonctionne avec les types 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;
}

Cette solution est sujette aux erreurs car elle dépend de l'ordre de déclaration des propriétés dans la classe T.
Vahid Ghadiri

10

Ce lien sur MSDN vaut le détour: Comment: implémenter CopyToDataTable <T> où le type générique T n'est pas un DataRow

Cela ajoute une méthode d'extension qui vous permet de faire ceci:

// 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 Merci, j'utilise ce code depuis des années sans problème jusqu'à présent. Mais comme je n'ai pas copié l'exemple de code de Microsoft et que je ne suis lié qu'au site Web, les autres solutions sont au moins plus conformes à la meilleure pratique de réponse stackoverflow.com/help/how-to-answer
Jürgen Steinblock

8

Une autre approche est la suivante:

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

Très très bien ... mais il a levé Exception de type 'System.OutOfMemoryException'. Je l'ai utilisé avec 500 000 objets ... Mais merci pour ça.
st_stefanov

C'est de loin la solution la plus propre que j'ai trouvée sur le net. Bon travail!
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
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant pourquoi et / ou comment ce code répond à la question améliore sa valeur à long terme.
kayess

Cette solution est sujette aux erreurs car elle dépend de l'ordre de déclaration des propriétés dans la classe T.
Vahid Ghadiri

6

Réponse de Marc Gravell mais en 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

essaye ça

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

J'ai écrit moi-même une petite bibliothèque pour accomplir cette tâche. Il utilise la réflexion uniquement pour la première fois qu'un type d'objet doit être traduit en table de données. Il émet une méthode qui fera tout le travail de traduction d'un type d'objet.

C'est extrêmement rapide. Vous pouvez le trouver ici: ModelShredder sur GoogleCode


2

J'ai également dû trouver une solution alternative, car aucune des options énumérées ici ne fonctionnait dans mon cas. J'utilisais un IEnumerable qui renvoyait un IEnumerable et les propriétés ne pouvaient pas être énumérées. Cela a fait l'affaire:

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

Je me rends compte que cela a été fermé pendant un certain temps; cependant, j'avais une solution à ce problème spécifique mais j'avais besoin d'une légère torsion: les colonnes et le tableau de données devaient être prédéfinis / déjà instanciés. Ensuite, je devais simplement insérer les types dans le tableau de données.

Voici donc un exemple de ce que j'ai fait:

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

pouvez-vous me montrer avec exemple. comment j'utilise la méthode d'extension pour les méthodes addtodataTable ()
Abhishek B.

Ce code contient déjà un exemple - jetez un œil à la méthode Main (). Le dernier bit de code a l'extension utilisée.
brenton

Pour plus d'informations, consultez cet article de MSDN sur les méthodes d'extension: msdn.microsoft.com/en-us/library/bb383977.aspx
brenton

2

Une réponse 2019 si vous utilisez .NET Core - utilisez la bibliothèque Nuget ToDataTable . Avantages:

  • De meilleures performances que FastMember
  • Crée également des SqlParameters structurés à utiliser en tant que paramètres table SQL Server

Clause de non - responsabilité - je suis l'auteur de ToDataTable

Performance - J'ai étendu certains tests Benchmark .Net et les ai inclus dans le référentiel ToDataTable . Les résultats sont les suivants:

Création d'une table de données de 100 000 lignes :

                           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

La méthode FastMember suggérée dans la réponse de Marc semblait plus mauvaise que la réponse de Mary qui utilisait la réflexion, mais j'ai lancé une autre méthode en utilisant un FastMember TypeAccessoret elle fonctionnait beaucoup mieux. Néanmoins, le package ToDataTable a surperformé le lot.


1

Si vous utilisez VB.NET, cette classe fait le travail.

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

si vous avez des propriétés dans votre classe, cette ligne de code est OK !!

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

mais si vous avez tous les champs publics, utilisez ceci:

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 réponse d'origine est d'en haut, je viens de modifier pour utiliser des champs au lieu de propriétés

et pour l'utiliser, faites cela

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

1

Pour convertir une liste générique en table de données, vous pouvez utiliser DataTableGenerator

Cette bibliothèque vous permet de convertir votre liste en un tableau de données avec des fonctionnalités multiples comme

  • Traduire l'en-tête du tableau de données
  • spécifier une colonne à afficher

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

Pour convertir une liste générique en DataTable

using Newtonsoft.Json;

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

0

Il s'agit de l'application console simple pour convertir la liste en table de données.

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

Se probó el método para that 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;
    }

3
Bienvenue dans Stack Overflow . Il s'agit d'un site Web anglophone, veuillez donc également écrire vos réponses en anglais.
Fairy

0
 Dim counties As New List(Of County)
 Dim dtCounties As DataTable
 dtCounties = _combinedRefRepository.Get_Counties()
 If dtCounties.Rows.Count <> 0 Then
    For Each row As DataRow In dtCounties.Rows
      Dim county As New County
      county.CountyId = row.Item(0).ToString()
      county.CountyName = row.Item(1).ToString().ToUpper()
      counties.Add(county)
    Next
    dtCounties.Dispose()
 End If

0

Je pense que c'est plus pratique et facile à utiliser.

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

0

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



0

Si vous souhaitez utiliser la réflexion et définir l'ordre des colonnes / inclure uniquement certaines colonnes / exclure certaines colonnes, essayez ceci:

        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++;
        }
    }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.