แปลงรายการทั่วไป / แจกแจงเป็น DataTable หรือไม่


261

ฉันมีวิธีการสองสามอย่างที่ส่งคืนรายการทั่วไปที่ต่างกัน

มีอยู่ใน. net ใด ๆ วิธีคงที่ระดับหรืออะไรก็ตามที่จะแปลงรายการใด ๆ ลงใน DataTable? สิ่งเดียวที่ฉันจินตนาการได้คือใช้ Reflection เพื่อทำสิ่งนี้

ถ้าฉันมีสิ่งนี้:

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

(รหัสต่อไปนี้ใช้ไม่ได้แน่นอน แต่ฉันต้องการมีความเป็นไปได้ที่:

DataTable dt = (DataTable) whatever;

2
แน่นอนคำถามที่ดีคือ "ทำไม" - เมื่อรายการ <T> ในหลายกรณีเป็นเครื่องมือที่ดีกว่า DataTable ;-P แต่ละของตัวเองผมคิดว่า ...
Marc Gravell

1
ฉันคิดว่าอันนี้อาจซ้ำกับคำถามนี้: stackoverflow.com/questions/523153/ ......มันมีคำตอบเหมือนกัน :-)
mezoid

2
@MarcGravell: ฉัน "ทำไม" คือการจัดการรายการ <T> (การข้ามคอลัมน์และแถว) ฉันกำลังพยายามทำ pivot จาก List <T> และเข้าถึงคุณสมบัติผ่านการสะท้อนกลับว่าเป็นความเจ็บปวด ฉันทำผิดหรือเปล่า?
Eduardo Molteni

1
@Eduardo มีเครื่องมือจำนวนมากที่จะลบความเจ็บปวดในการสะท้อนนั่น - FastMember ก้าวกระโดดสู่ใจ มันอาจจะยังเป็นไปได้ว่า DataTable เป็นประโยชน์กับสถานการณ์ที่เฉพาะเจาะจง - มันทั้งหมดขึ้นอยู่กับบริบท บางทีปัญหาที่ใหญ่ที่สุดคือผู้ใช้ DataTable สำหรับการจัดเก็บข้อมูลทั้งหมดเพียงเพราะมีอยู่โดยไม่ต้องสละเวลาพิจารณาตัวเลือกและสถานการณ์ของพวกเขา
Marc Gravell

@EduardoMolteni หากคุณสนใจฉันอัปเดต FastMember เพื่อรับการสนับสนุนโดยตรงจากสิ่งนี้ - ดูคำตอบที่อัปเดต
Marc Gravell

คำตอบ:


325

นี่คือการอัปเดต 2013 ที่ดีโดยใช้FastMemberจาก NuGet:

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

สิ่งนี้ใช้ API การเขียนโปรแกรมเมตาของ FastMember เพื่อประสิทธิภาพสูงสุด หากคุณต้องการ จำกัด ให้เฉพาะสมาชิก (หรือบังคับใช้คำสั่งซื้อ) คุณก็สามารถทำได้เช่นกัน:

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

บรรณาธิการDis / claimer: FastMember เป็นโครงการ Marc Gravell ทองคำและแมลงวันเต็มไปหมด!


ใช่นี้สวยมากตรงข้ามแน่นอนของนี้หนึ่ง; ภาพสะท้อนจะพอเพียง - หรือถ้าคุณต้องการได้เร็วขึ้นHyperDescriptorใน 2.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;        
}

ขณะนี้มีหนึ่งบรรทัดคุณสามารถทำให้เร็วกว่าการสะท้อนหลายเท่า (โดยการเปิดใช้งานHyperDescriptorชนิดวัตถุ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 มาก สำหรับต้องการบางสิ่งบางอย่างเร็ว + ผลการเรียนรู้ ขอบคุณมาร์ค!
Elisabeth

11
@Ellesedil ฉันพยายามอย่างหนักที่จะจำสิ่งต่าง ๆ เหล่านี้อย่างชัดเจน แต่เนื่องจากฉันไม่ได้ขายอะไร (แต่ฉันทำงานหลายชั่วโมงอย่างอิสระ) ฉันยอมรับว่าฉันไม่รู้สึกผิดมากที่นี่ ...
Marc Gravell

2
วิธีการของคุณ ToDataTable ไม่รองรับฟิลด์ nullable: ข้อมูลเพิ่มเติม: ชุดข้อมูลไม่รองรับ System.Nullable <>
Dainius Kreivys

235

ฉันต้องแก้ไขโค้ดตัวอย่างของ Marc Gravell เพื่อจัดการประเภท nullable และค่า null ฉันได้รวมเวอร์ชันที่ใช้งานได้ด้านล่าง ขอบคุณมาร์ค

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

นี่คือคำตอบที่ยอดเยี่ยม ฉันชอบที่จะเห็นตัวอย่างนี้ขยายออกเพื่อจัดการกลุ่มโดยรายการที่จะมีคุณสมบัติของรายการและมีคอลัมน์ที่สร้างขึ้นในลักษณะเดียวกันข้างต้น
Coder ที่ไม่รู้จัก

2
เพื่อให้บรรลุถึง @Jim Beam ให้เปลี่ยนวิธีการลงลายมือชื่อเพื่อยอมรับการกลับมาของ GroupBy: public static DataTable ToDataTable<TKey, T>(this IEnumerable<IGrouping<TKey, T>> data) จากนั้นเพิ่มคอลัมน์พิเศษก่อน foreach loop: table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); จากนั้นเพิ่ม loop รอบ data loop ที่คุณวนกลุ่ม: foreach (IGrouping <TKey, T> จัดกลุ่มในข้อมูล) {foreach (รายการ T ในกลุ่มรายการ) {ดู GIST นี้สำหรับรายละเอียดทั้งหมด: gist.github.com/rickdailey/8679306
Rick Dailey

เฮ้มีวิธีจัดการวัตถุด้วยวัตถุภายในหรือไม่ ฉันแค่ต้องการให้คุณสมบัติภายในปรากฏเป็นคอลัมน์หลังจากคอลัมน์ของวัตถุหลัก
heyNow

@heyNow ฉันแน่ใจว่ามี แต่ฉันไม่จำเป็นต้องใช้ฟังก์ชั่นนั้นกับสิ่งที่ฉันทำและปล่อยให้คนอื่นขยายออกไป :)
Mary Hamlin

1
นี่คือโพสต์เก่าดังนั้นไม่แน่ใจว่าความคิดเห็นนี้มีประโยชน์เพียงใด แต่มีข้อผิดพลาดส่อเสียดในToDataTableวิธีนี้ หากTการดำเนินการอินเตอร์เฟซที่อาจจะกลับมาประเภทอินเตอร์เฟซที่มากกว่าระดับที่เกิดขึ้นจริงของวัตถุที่เกิดในที่ว่างเปล่าtypeof(T) DataTableการแทนที่ด้วยdata.First().GetType()ควรแก้ไข
ลูคัส

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

วิธีทำรายการ <int>
Muflix

1
วิธีการด้านบนจะใช้ได้กับ int (และประเภทค่าอื่น ๆ ด้วย) ... int เป็นประเภทค่า ดู: msdn.microsoft.com/en-us/library/s1ax56ch.aspx
Onur Omer

ฉันชอบสิ่งนี้เพราะมันไม่ได้ขึ้นอยู่กับการใช้วิธีการขยาย ใช้งานได้ดีสำหรับรหัสเก่าที่อาจไม่สามารถเข้าถึงวิธีการส่วนขยายได้
webworm

13

นี่คือการผสมผสานที่ง่ายของการแก้ปัญหา มันทำงานได้กับ Nullable types

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 มีค่าเข้าชม: วิธีการ: ใช้ CopyToDataTable <T> โดยที่ T ประเภททั่วไปไม่ใช่ DataRow

นี่เป็นการเพิ่มวิธีส่วนขยายที่ให้คุณทำสิ่งนี้:

// 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' ฉันใช้มันกับ 500,000 รายการ ... แต่ขอบคุณสำหรับสิ่งนี้
st_stefanov

นี่คือทางออกที่สะอาดที่สุดที่ฉันพบบนอินเทอร์เน็ต การทำงานที่ดี!
ซาร่าห์

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

ฉันได้เขียนห้องสมุดเล็ก ๆ เพื่อทำงานนี้ให้สำเร็จ มันใช้การสะท้อนเท่านั้นเป็นครั้งแรกที่ชนิดของวัตถุจะถูกแปลเป็น datatable มันปล่อยวิธีการที่จะทำงานทั้งหมดแปลวัตถุชนิด

มันเร็วอย่างเห็นได้ชัด คุณสามารถค้นหาได้ที่นี่: ModelShredder บน GoogleCode


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.

รหัสนี้มีตัวอย่างอยู่แล้ว - ดูที่วิธีการหลัก () บิตสุดท้ายของรหัสมีการขยายการใช้งาน
Brenton

สำหรับการอ่านเพิ่มเติมโปรดดูที่บทความนี้จาก MSDN เกี่ยวกับวิธีการขยาย: msdn.microsoft.com/en-us/library/bb383977.aspx
brenton

2

2019 คำตอบถ้าคุณกำลังใช้ .NET หลัก - ใช้ห้องสมุด Nuget ToDataTable ข้อดี:

ข้อจำกัดความรับผิดชอบ - ฉันเป็นผู้เขียน ToDataTable

ผลการดำเนินงาน - ฉันครอบคลุมขึ้นบางเกณฑ์มาตรฐาน .Netทดสอบและรวมถึงพวกเขาในrepo ToDataTable ผลการวิจัยพบว่า

สร้าง 100,000 DataTable แถว :

                           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

วิธีการ FastMember ที่แนะนำในคำตอบของ Marcดูเหมือนจะแย่กว่าคำตอบของ 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

ในการแปลงรายการ Generic เป็น 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

นี่คือแอปพลิเคชั่นคอนโซลที่ใช้ง่ายในการแปลงรายการเป็นข้อมูล

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

ดูเหมือนว่าสำหรับปัญหาที่เกิดขึ้นกับการตั้งค่าเป็นโมฆะ

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

รายการ / ข้อมูล = รายการใหม่ (); var dataDT = Newtonsoft.Json.JsonConvert.DeserializeObject (Newtonsoft.Json.JsonConvert.SerializeObject (ข้อมูล));



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.