일반 목록 / 열거 가능을 DataTable로 변환 하시겠습니까?


261

다른 일반 목록을 반환하는 메서드가 거의 없습니다.

.net에 클래스 정적 메소드가 있거나 목록을 데이터 테이블로 변환해야합니까? 내가 상상할 수있는 유일한 것은 Reflection을 사용 하여이 작업을 수행하는 것입니다.

내가 이것을 가지고 있다면 :

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

(이 다음 코드는 물론 작동하지 않지만 가능한 가능성을 갖고 싶습니다.

DataTable dt = (DataTable) whatever;

2
물론 좋은 질문은 "왜?"입니다. -List <T>가 많은 경우에 DataTable보다 나은 도구 일 때 ;-p 각각 자신의 것으로 생각합니다.
Marc Gravell

1
나는 이것이이 질문과 중복 될 수 있다고 생각합니다 : stackoverflow.com/questions/523153/… 그것은 거의 동일한 대답을 가지고 있습니다. :-)
mezoid

2
@MarcGravell : 나의 "왜?" List <T> 조작 (열 및 행 탐색)입니다. List <T>에서 피벗을 만들고 반사를 통해 속성에 액세스하려고하면 고통 스럽습니다. 내가 잘못하고있어?
Eduardo Molteni

1
@Eduardo는 거기에 반사 고통을 제거하는 도구가 많이 있습니다-FastMember가 떠 오릅니다. 그것은 수있는 모든 컨텍스트에 따라 달라집니다 - 또한 DataTable을 특정 시나리오에 유용하다는 것을합니다. 아마도 가장 큰 문제는 모든 데이터 저장에 DataTable을를 사용하는 사람입니다 이 존재해서 옵션과 자신의 시나리오를 고려해야 할 시간을내어없이.
Marc Gravell

@EduardoMolteni 당신이 관심이 있다면, 나는 이것을 직접 지원하기 위해 FastMember를 업데이트했습니다-업데이트 된 답변을 참조하십시오
Marc Gravell

답변:


325

NuGet의 FastMember 를 사용한 멋진 2013 업데이트는 다음과 같습니다 .

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

이것은 최대 성능을 위해 FastMember의 메타 프로그래밍 API를 사용합니다. 특정 회원으로 제한하거나 주문을 시행하려면 다음을 수행하십시오.

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

편집자 Dis / 클레임 : FastMember는 Marc Gravell 프로젝트입니다. 그 금과 완전 비행!


예, 이것은의 거의 정반대 하나; 리플렉션으로 충분하거나 HyperDescriptor2.0, Expression3.5 또는 더 빠른 속도가 필요할 경우 실제로, HyperDescriptor그 이상이어야합니다.

예를 들면 다음과 같습니다.

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

이제 한 줄을 사용하면 ( HyperDescriptorobject-type 을 활성화하여) 반사보다 여러 배 더 빠르게 만들 수 있습니다 T.


재 성능 쿼리 편집; 다음은 결과가 포함 된 테스트 리그입니다.

Vanilla 27179
Hyper   6997

병목 현상이 멤버 액세스에서 DataTable성능 으로 전환 된 것으로 의심 됩니다.

암호:

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
글쎄 "있는 그대로", 그것은 반사만큼 빠르다. HyperDescriptor를 활성화하면 리플렉션 핸드가 다운됩니다. 빠른 테스트를 실행합니다 ... (2 분)
Marc Gravell

3.5에 대한 표현이 언급되었다. 코드에 어떻게 영향을 주면 샘플이 있습니까?
MicMit

3
@MarcGravell 예 Expression 솔루션에 관심이 많습니다. 빠른 + 학습 효과가 필요합니다. 고마워 마크!
엘리자베스

11
@Ellesedil 나는 그런 것들을 명시 적으로 공개하는 것을 기억하려고 노력하지만, 아무것도 팔지 않기 때문에 (그러나 많은 시간 동안 자유롭게 일할 수있는 일을하고있다) 나는 여기에 엄청난 죄책감을 느끼지 않는다고 고백한다 .
Marc Gravell

2
ToDataTable 메소드는 널 입력 가능 필드를 지원하지 않습니다. 추가 정보 : DataSet은 System.Nullable <>을 지원하지 않습니다.
Dainius Kreivys

235

nullable 유형과 null 값을 처리하기 위해 Marc Gravell의 샘플 코드를 수정해야했습니다. 아래에 작동 버전을 포함 시켰습니다. 고마워 마크.

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

이것은 훌륭한 답변입니다. 이 예제가 항목 속성을 포함하고 위와 동일한 방식으로 열이 생성되는 목록을 기준으로 그룹을 처리하기 위해 확장되는 것을보고 싶습니다.
알 수없는 코더

2
@Jim Beam을 달성하려면 GroupBy의 리턴을 승인하도록 메소드 서명을 변경하십시오. public static DataTable ToDataTable<TKey, T>(this IEnumerable<IGrouping<TKey, T>> data) 그런 다음 foreach 루프 앞에 추가 열 table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); 을 추가하고 그룹을 반복하는 데이터 루프 주위에 루프를 추가하십시오. foreach (IGrouping <TKey, : T> 데이터 그룹) group.Items의 {foreach는 (T 항목) {자세한 내용은이 GIST 참조 gist.github.com/rickdailey/8679306
릭 데일리

내부 객체로 객체를 처리하는 방법이 있습니까? 난 그냥 내부 속성은 부모 개체의 열 후 열로 표시 할
heyNow

@ heyNow, 나는 확신합니다. 그러나 나는 내가하고있는 일에 그 기능이 필요하지 않았으므로 다른 사람이 확장 할 수 있도록 남겨 두었습니다. :)
Mary Hamlin

1
이것은 오래된 게시물 이므로이 의견이 얼마나 유용한 지 잘 모르겠지만이 방법에는 부적절한 버그가 ToDataTable있습니다. T인터페이스를 구현하는 경우 typeof(T)객체의 실제 클래스가 아닌 인터페이스 유형을 반환하여 비어있는 결과를 낳을 수 DataTable있습니다. 교체하면 data.First().GetType()문제가 해결됩니다.
Lucas

14

데이터 테이블 과 같은 값 유형으로 작동 하도록 Marc의 답변 을 약간 변경했습니다 List<string>.

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

List <int>에 대해 만드는 방법?
Muflix

1
위의 방법은 int (및 다른 값 유형)에서도 작동합니다 ... int는 값 유형입니다. 참조 : msdn.microsoft.com/en-us/library/s1ax56ch.aspx
Onur Omer

확장 방법 사용에 의존하지 않기 때문에 이것을 좋아합니다. 확장 메소드에 액세스 할 수없는 이전 코드베이스에 적합합니다.
webworm

13

이것은 솔루션의 간단한 혼합입니다. 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;
}

이 솔루션은 T 클래스의 속성 선언 순서에 따라 다르기 때문에 오류가 발생하기 쉽습니다.
Vahid Ghadiri

10

MSDN의이 링크는 방문 할 가치가 있습니다. 방법 : 일반 형식 T가 DataRow가 아닌 ​​CopyToDataTable <T> 구현

이렇게하면 확장 방법이 추가됩니다.

// 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 감사합니다. 지금까지 아무런 문제없이이 코드를 몇 년 동안 사용했습니다. 그러나 Microsoft에서 예제 코드를 복사하지 않고 웹 사이트에만 링크했기 때문에 다른 솔루션은 적어도 모범 사례 답변 stackoverflow.com/help/how-to-answer
Jürgen Steinblock

8

다른 접근법은 위와 같습니다.

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

매우 우수하지만 'System.OutOfMemoryException'유형의 Exception이 발생했습니다. 나는 500,000 항목과 함께 사용했지만 ... 감사합니다.
st_stefanov

이것은 내가 인터넷에서 찾은 가장 깨끗한 솔루션입니다. 훌륭한 일!
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
이 코드는 질문에 대답 할 수 있지만,이 코드가 질문에 응답하는 이유 및 / 또는 방법에 대한 추가 컨텍스트를 제공하면 장기적인 가치가 향상됩니다.
kayess

이 솔루션은 T 클래스의 속성 선언 순서에 따라 다르기 때문에 오류가 발생하기 쉽습니다.
Vahid Ghadiri

6

Marc Gravell의 답변이지만 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

이 시도

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

이 작업을 수행하기 위해 작은 라이브러리를 직접 작성했습니다. 오브젝트 유형이 데이터 테이블로 처음 변환 될 때만 리플렉션을 사용합니다. 객체 유형을 번역하는 모든 작업을 수행하는 메소드를 생성합니다.

빠른 속도입니다. 여기에서 찾을 수 있습니다 : GoogleCode의 ModelShredder


2

여기에 나열된 옵션 중 어느 것도 내 경우에 효과가 없었기 때문에 대체 솔루션을 생각해 내야했습니다. IEnumerable을 반환하는 IEnumerable을 사용하고 있었고 속성을 열거 할 수 없었습니다. 이것은 트릭을했다 :

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

나는 이것이 한동안 폐쇄되었다는 것을 알고있다. 그러나이 특정 문제에 대한 해결책이 있었지만 약간의 왜곡이 필요했습니다. 열과 데이터 테이블은 사전 정의 / 이미 인스턴스화해야했습니다. 그런 다음 단순히 데이터 테이블에 유형을 삽입해야했습니다.

여기 내가 한 일의 예가 있습니다.

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

예를 들어 주시겠습니까? 어떻게 (addtodataTable에 대한 확장 방법을 사용) 방법
Abhishek B.

이 코드에는 이미 예제가 있습니다. Main () 메서드를 살펴보십시오. 코드의 마지막 비트는 확장명이 사용되고 있습니다.
brenton

자세한 내용은 MSDN에서 확장 방법에 대한이 기사를 참조하십시오. msdn.microsoft.com/en-us/library/bb383977.aspx
brenton

2

.NET Core를 사용하는 경우 2019 답변 -Nuget ToDataTable 라이브러리를 사용하십시오 . 장점 :

면책 조항 -저는 ToDataTable의 저자입니다

성능 -일부 Benchmark .Net 테스트를 수행하여 ToDataTable 리포지토리에 포함 시켰습니다 . 결과는 다음과 같습니다.

100,000 행 데이터 테이블 만들기 :

                           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

Marc의 답변 에서 제안 된 FastMember 방법은 리플렉션을 사용하는 Mary의 답변 보다 성능이 좋지 않은 것처럼 보였지만 FastMember를 사용하여 다른 방법을 사용하여 TypeAccessor훨씬 더 나은 성능을 보였습니다 . 그럼에도 불구하고 ToDataTable 패키지가 성능을 능가했습니다.


1

VB.NET을 사용하는 경우이 클래스가 작업을 수행합니다.

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

클래스에 속성이있는 경우이 코드 줄은 괜찮습니다 !!

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

그러나 모든 공개 필드가 있으면 다음을 사용하십시오.

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

원래 답변은 위에서 왔으며 속성 대신 필드를 사용하도록 편집했습니다.

그리고 이것을 사용하려면

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

1

일반 목록을 데이터 테이블로 변환하려면 DataTableGenerator를

이 라이브러리를 사용하면 다음과 같은 다중 기능을 사용하여 목록을 데이터 테이블로 변환 할 수 있습니다

  • 데이터 테이블 헤더 번역
  • 표시 할 열을 지정하십시오.

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

일반 목록을 DataTable로 변환하려면

Newtonsoft.Json 사용;

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

0

이것은 List를 Datatable로 변환하는 간단한 콘솔 응용 프로그램입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.ComponentModel;

namespace ConvertListToDataTable
{
    public static class Program
    {
        public static void Main(string[] args)
        {
            List<MyObject> list = new List<MyObject>();
            for (int i = 0; i < 5; i++)
            {
                list.Add(new MyObject { Sno = i, Name = i.ToString() + "-KarthiK", Dat = DateTime.Now.AddSeconds(i) });
            }

            DataTable dt = ConvertListToDataTable(list);
            foreach (DataRow row in dt.Rows)
            {
                Console.WriteLine();
                for (int x = 0; x < dt.Columns.Count; x++)
                {
                    Console.Write(row[x].ToString() + " ");
                }
            }
            Console.ReadLine();
        }

        public class MyObject
        {
            public int Sno { get; set; }
            public string Name { get; set; }
            public DateTime Dat { get; set; }
        }

        public static DataTable ConvertListToDataTable<T>(this List<T> iList)
        {
            DataTable dataTable = new DataTable();
            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));
            for (int i = 0; i < props.Count; i++)
            {
                PropertyDescriptor propertyDescriptor = props[i];
                Type type = propertyDescriptor.PropertyType;

                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                    type = Nullable.GetUnderlyingType(type);

                dataTable.Columns.Add(propertyDescriptor.Name, type);
            }
            object[] values = new object[props.Count];
            foreach (T iListItem in iList)
            {
                for (int i = 0; i < values.Length; i++)
                {
                    values[i] = props[i].GetValue(iListItem);
                }
                dataTable.Rows.Add(values);
            }
            return dataTable;
        }
    }
}

0

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

2
스택 오버플로에 오신 것을 환영합니다 . 이 사이트는 영어를 사용하는 웹 사이트이므로 영어로 답변을 작성하십시오.
요정

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

더 편리하고 사용하기 쉽다고 생각합니다.

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

0

List / data = 새 목록 (); var dataDT = Newtonsoft.Json.JsonConvert.DeserializeObject (Newtonsoft.Json.JsonConvert.SerializeObject (data));



0

리플렉션을 사용하고 열 순서를 설정하고 / 일부 열만 포함 / 일부 열을 제외하려면 다음을 시도하십시오.

        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++;
        }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.