Chuyển đổi danh sách chung / Số lượng thành DataTable?


261

Tôi có một vài phương thức trả về Danh sách chung khác nhau.

Tồn tại trong .net bất kỳ phương thức tĩnh lớp nào hoặc bất cứ điều gì để chuyển đổi bất kỳ danh sách thành dữ liệu? Điều duy nhất mà tôi có thể tưởng tượng là sử dụng Reflection để làm điều này.

NẾU tôi có cái này:

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

(Tất nhiên mã tiếp theo này không hoạt động, nhưng tôi muốn có khả năng:

DataTable dt = (DataTable) whatever;

2
Tất nhiên, một câu hỏi hay sẽ là "tại sao?" - khi Danh sách <T> trong nhiều trường hợp là một công cụ tốt hơn DataTable ;-p Mỗi công cụ của riêng họ, tôi đoán ...
Marc Gravell

1
Tôi nghĩ rằng câu hỏi này có thể là một bản sao của câu hỏi này: stackoverflow.com/questions/523153/NH Nó thậm chí còn có một câu trả lời gần giống nhau. :-)
mezoid

2
@MarcGravell: "Tại sao?" là danh sách <T> thao tác (Traversing cột & hàng). Tôi đang cố gắng tạo một trục từ Danh sách <T> và truy cập các thuộc tính thông qua phản xạ đó là một nỗi đau. Tôi đang làm sai?
Eduardo Molteni

1
@Eduardo có bất kỳ số lượng công cụ để loại bỏ nỗi đau phản chiếu ở đó - FastMember nhảy vào tâm trí. Cũng có thể là DataTable hữu ích cho các kịch bản cụ thể - tất cả phụ thuộc vào ngữ cảnh. Có lẽ vấn đề lớn nhất là mọi người sử dụng DataTable cho tất cả lưu trữ dữ liệu chỉ vì nó tồn tại mà không dành thời gian để xem xét các tùy chọn và kịch bản của chúng.
Marc Gravell

@EduardoMolteni nếu bạn quan tâm, tôi đã cập nhật FastMember để được hỗ trợ trực tiếp cho việc này - xem câu trả lời được cập nhật
Marc Gravell

Câu trả lời:


325

Đây là một bản cập nhật 2013 tuyệt vời sử dụng FastMember từ NuGet:

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

Điều này sử dụng API lập trình meta của FastMember để có hiệu suất tối đa. Nếu bạn muốn hạn chế nó cho các thành viên cụ thể (hoặc thực thi lệnh), thì bạn cũng có thể làm điều đó:

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

Dis / aimer của biên tập viên : FastMember là một dự án của Marc Gravell. Vàng và bay đầy đủ của nó!


Vâng, điều này hoàn toàn trái ngược với điều này ; sự phản chiếu sẽ đủ - hoặc nếu bạn cần nhanh hơn, HyperDescriptortrong 2.0 hoặc có thể Expressiontrong 3.5. Trên thực tế, HyperDescriptornên được nhiều hơn đầy đủ.

Ví dụ:

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

Bây giờ với một dòng, bạn có thể thực hiện việc này nhanh hơn nhiều lần so với phản chiếu (bằng cách bật HyperDescriptorloại đối tượng T).


chỉnh sửa lại truy vấn hiệu suất; đây là một thử nghiệm với kết quả:

Vanilla 27179
Hyper   6997

Tôi nghi ngờ rằng nút cổ chai đã chuyển từ quyền truy cập của thành viên sang DataTablehiệu suất ... Tôi nghi ngờ bạn sẽ cải thiện nhiều về điều đó ...

mã:

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
Chà "như là", nó sẽ nhanh như phản xạ. Nếu bạn bật HyperDescriptor, nó sẽ hạ gục bàn tay phản chiếu ... Tôi sẽ chạy thử nghiệm nhanh ... (2 phút)
Marc Gravell

Biểu hiện đã được đề cập cho 3,5. Nếu được sử dụng, nó sẽ ảnh hưởng đến mã như thế nào, có mẫu nào không?
MicMit

3
@MarcGravell Có, tôi sẽ rất quan tâm đến giải pháp Biểu hiện. Vì cần một cái gì đó nhanh + hiệu quả học tập. Cảm ơn bạn Marc!
Elisabeth

11
@Ellesedil Tôi cố gắng hết sức để nhớ rõ ràng tiết lộ những điều như vậy, nhưng vì tôi không bán bất cứ thứ gì (mà là làm cho nhiều giờ làm việc có sẵn miễn phí) Tôi thú nhận rằng tôi không cảm thấy tội lỗi ở đây ...
Marc Gravell

2
phương thức của bạn ToDataTable không hỗ trợ các trường nullable: Thông tin bổ sung: Dataset không hỗ trợ System.Nullable <>.
Dainius Kreivys

235

Tôi đã phải sửa đổi mã mẫu của Marc Gravell để xử lý các loại null và giá trị null. Tôi đã bao gồm một phiên bản làm việc dưới đây. Cảm ơn 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;
}

Đây là một câu trả lời tuyệt vời. Tôi rất thích xem ví dụ này được mở rộng để xử lý một nhóm theo danh sách có chứa thuộc tính vật phẩm và có các cột được tạo theo cùng một cách ở trên.
không xác định

2
Để đạt được @Jim Beam đó, hãy thay đổi chữ ký phương thức để chấp nhận trả về GroupBy: public static DataTable ToDataTable<TKey, T>(this IEnumerable<IGrouping<TKey, T>> data) Sau đó, thêm một cột phụ trước vòng lặp foreach: table.Columns.Add("Key", Nullable.GetUnderlyingType(typeof(TKey)) ?? typeof(TKey)); Và sau đó thêm một vòng lặp quanh vòng lặp dữ liệu nơi bạn lặp lại các nhóm: foreach (IGrouping <TKey, T> nhóm trong dữ liệu) {foreach (T item in group.Items) {Xem GIST này để biết chi tiết đầy đủ: gist.github.com/rickdailey/8679306
Rick

này, có cách nào để xử lý một đối tượng với các đối tượng bên trong không? Tôi chỉ muốn các Thuộc tính bên trong xuất hiện dưới dạng các cột sau các cột của đối tượng cha mẹ
heyNow

@heyNow, tôi chắc chắn là có. Nhưng tôi không thực sự có nhu cầu về chức năng đó với những gì tôi đang làm và vì vậy để lại cho người khác mở rộng. :)
Mary Hamlin

1
Đây là một bài viết cũ nên không chắc bình luận này hữu ích như thế nào, nhưng có một lỗi lén lút trong ToDataTablephương pháp này . Nếu Tthực hiện một giao diện typeof(T)có thể trả về loại giao diện chứ không phải lớp thực tế của đối tượng, dẫn đến trống DataTable. Thay thế nó data.First().GetType()nên sửa chữa nó.
Lucas

14

Một thay đổi nhỏ đối với câu trả lời của Marc để làm cho nó hoạt động với các loại giá trị như List<string>bảng dữ liệu:

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

Làm cách nào để tạo danh sách cho Danh sách <int>?
Muflix

1
Phương thức trên sẽ hoạt động cho int (và các loại giá trị khác) quá ... int là một loại giá trị. xem: msdn.microsoft.com/en-us/l Library / s1ax56ch.aspx
Onur Omer

Tôi thích điều này bởi vì nó không phụ thuộc vào việc sử dụng một phương thức mở rộng. Hoạt động tốt đối với các cơ sở mã cũ có thể không có quyền truy cập vào Phương thức mở rộng.
giun web

13

Đây là một kết hợp đơn giản của các giải pháp. Nó hoạt động với các loại 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;
}

Giải pháp này dễ bị lỗi vì nó phụ thuộc vào thứ tự khai báo thuộc tính trong lớp T.
Vahid Ghadiri

10

Liên kết này trên MSDN đáng để truy cập: Cách: Triển khai CopyToDataTable <T> Trong đó Loại chung T không phải là DataRow

Điều này thêm một phương thức mở rộng cho phép bạn làm điều này:

// 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 Cảm ơn, tôi đã sử dụng mã này trong nhiều năm mà không gặp vấn đề gì. Nhưng vì tôi đã không sao chép mã ví dụ từ microsoft và chỉ liên kết với trang web, các giải pháp khác ít nhất phù hợp hơn với câu trả lời thực tiễn tốt nhất stackoverflow.com/help/how-to-answer
Jürgen Steinblock

8

Một cách tiếp cận khác là ở trên:

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

Rất rất tốt ... nhưng nó đã ném Ngoại lệ của loại 'System.OutOfMemoryException'. Tôi đã sử dụng nó với 500 000 mặt hàng ... Nhưng cảm ơn bạn vì điều này.
st_stefanov

Đây là giải pháp sạch nhất tôi đã tìm thấy trên mạng. Công việc tuyệt vời
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
Mặc dù mã này có thể trả lời câu hỏi, cung cấp ngữ cảnh bổ sung về lý do và / hoặc cách mã này trả lời câu hỏi cải thiện giá trị lâu dài của nó.
kayess

Giải pháp này dễ bị lỗi vì nó phụ thuộc vào thứ tự khai báo thuộc tính trong lớp T.
Vahid Ghadiri

6

Câu trả lời của Marc Gravell nhưng trong 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

thử cái này

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

Tôi đã tự viết một thư viện nhỏ để hoàn thành nhiệm vụ này. Nó chỉ sử dụng sự phản chiếu cho lần đầu tiên một loại đối tượng được dịch sang một dữ liệu. Nó phát ra một phương thức sẽ thực hiện tất cả công việc dịch một loại đối tượng.

Nó rạo rực. Bạn có thể tìm thấy nó ở đây: ModelShredder trên GoogleCode


2

Tôi cũng đã phải đưa ra một giải pháp thay thế, vì không có lựa chọn nào được liệt kê ở đây hoạt động trong trường hợp của tôi. Tôi đã sử dụng một IEnumerable trả về một IEnumerable và các thuộc tính không thể được liệt kê. Điều này đã lừa

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

Tôi nhận ra rằng điều này đã bị đóng cửa trong một thời gian; tuy nhiên, tôi đã có một giải pháp cho vấn đề cụ thể này nhưng cần một bước ngoặt nhỏ: các cột và bảng dữ liệu cần được xác định trước / đã được khởi tạo. Sau đó, tôi chỉ cần chèn các loại vào bảng dữ liệu.

Vì vậy, đây là một ví dụ về những gì tôi đã làm:

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

bạn có thể chỉ cho tôi với ví dụ. làm thế nào tôi sử dụng phương pháp mở rộng cho addtodataTable () phương pháp
Abhishek B.

Mã này đã có một ví dụ trong đó rồi - hãy xem phương thức Main (). Đoạn mã cuối cùng có phần mở rộng đang được sử dụng.
brenton

Để đọc thêm, xin vui lòng xem bài viết này từ MSDN về các phương pháp mở rộng: msdn.microsoft.com/en-us/l
Library / bb383977.aspx

2

Câu trả lời năm 2019 nếu bạn đang sử dụng .NET Core - sử dụng thư viện Nuget ToDataTable . Ưu điểm:

Tuyên bố miễn trừ trách nhiệm - Tôi là tác giả của ToDataTable

Hiệu suất - Tôi mở rộng một số bài kiểm tra Điểm chuẩn .Net và đưa chúng vào repo ToDataTable . Kết quả như sau:

Tạo 100.000 hàng có thể truy cập :

                           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

Phương pháp FastMember được đề xuất trong câu trả lời của Marc dường như hoạt động kém hơn câu trả lời của Mary sử dụng sự phản chiếu, nhưng tôi đã thực hiện một phương pháp khác bằng cách sử dụng FastMember TypeAccessorvà nó hoạt động tốt hơn nhiều. Tuy nhiên, gói ToDataTable vượt trội hơn rất nhiều.


1

Nếu bạn đang sử dụng VB.NET thì lớp này thực hiện công việc.

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

nếu bạn có thuộc tính trong lớp thì dòng mã này là OK !!

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

nhưng nếu bạn có tất cả các lĩnh vực công cộng thì hãy sử dụng điều này:

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

câu trả lời ban đầu là từ phía trên, tôi chỉ chỉnh sửa để sử dụng các trường thay vì các thuộc tính

và sử dụng nó làm điều này

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

1

Để chuyển đổi danh sách chung sang bảng dữ liệu, bạn có thể sử dụng DataTableGenerator

Thư viện này cho phép bạn chuyển đổi danh sách của mình thành một bảng dữ liệu với nhiều tính năng như

  • Dịch tiêu đề bảng dữ liệu
  • chỉ định một số cột để hiển thị

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

Để chuyển đổi danh sách chung thành DataTable

sử dụng Newtonsoft.Json;

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

0

Đây là Ứng dụng Console đơn giản để chuyển đổi Danh sách thành Đạt được.

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
Chào mừng bạn đến với Stack Overflow . Đây là một trang web nói tiếng Anh, vì vậy hãy viết câu trả lời của bạn bằng tiếng Anh.
Tiên

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

Tôi nghĩ rằng nó thuận tiện hơn và dễ sử dụng.

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

0

Danh sách / dữ liệu = Danh sách mới (); var dataDT = Newtonsoft.Json.JsonConvert.DeserializeObject (Newtonsoft.Json.JsonConvert.SerializeObject (data));



0

Nếu bạn muốn sử dụng sự phản chiếu và đặt thứ tự cột / chỉ bao gồm một số cột / Loại trừ một số cột hãy thử điều này:

        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++;
        }
    }
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.