.NET - Chuyển đổi Bộ sưu tập Chung thành DataTable


76

Tôi đang cố gắng chuyển đổi một tập hợp chung (Danh sách) thành một DataTable. Tôi đã tìm thấy mã sau để giúp tôi thực hiện việc này:

// Sorry about indentation
public class CollectionHelper
{
private CollectionHelper()
{
}

// this is the method I have been using
public static DataTable ConvertTo<T>(IList<T> list)
{
    DataTable table = CreateTable<T>();
    Type entityType = typeof(T);
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);

    foreach (T item in list)
    {
        DataRow row = table.NewRow();

        foreach (PropertyDescriptor prop in properties)
        {
            row[prop.Name] = prop.GetValue(item);
        }

        table.Rows.Add(row);
    }

    return table;
}    

public static DataTable CreateTable<T>()
{
    Type entityType = typeof(T);
    DataTable table = new DataTable(entityType.Name);
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entityType);

    foreach (PropertyDescriptor prop in properties)
    {
        // HERE IS WHERE THE ERROR IS THROWN FOR NULLABLE TYPES
        table.Columns.Add(prop.Name, prop.PropertyType);
    }

    return table;
}
}

Vấn đề của tôi là khi tôi thay đổi một trong các thuộc tính của MySimpleClass thành kiểu nullable, tôi gặp lỗi sau:

DataSet does not support System.Nullable<>.

Làm cách nào để thực hiện việc này với các thuộc tính / trường Nullable trong lớp của tôi?

Câu trả lời:


139

Sau đó, có lẽ bạn sẽ cần nâng chúng lên dạng không thể nullable, sử dụng Nullable.GetUnderlyingTypevà có thể thay đổi một vài nullgiá trị thành DbNull.Value...

Thay đổi nhiệm vụ thành:

row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;

và khi thêm các cột là:

table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(
            prop.PropertyType) ?? prop.PropertyType);

Và nó hoạt động. ( ??là toán tử kết hợp null; nó sử dụng toán hạng đầu tiên nếu nó không phải là null, nếu không toán hạng thứ hai được đánh giá và sử dụng)


62
Khi lớn lên, tôi muốn trở thành Marc Gravell.
Ronnie Overby

2
Ừm. Toán tử kết hợp rỗng. Rất đẹp.
J. Steen

4
Trong VB, sử dụng IF (Nullable.GetUnderlyingType (prop.PropertyType), prop.PropertyType)
GilShalit

5

Tốt. Vì DataSet không hỗ trợ các kiểu nullable, nên bạn phải kiểm tra xem thuộc tính có phải là kiểu chung hay không, lấy định nghĩa chung của kiểu đó và sau đó lấy đối số (là kiểu thực) bằng cách sử dụng, có lẽ Nullable.GetUnderlyingType,. Nếu giá trị là null, chỉ cần sử dụng DBNull.Valuetrong DataSet.


5

Nếu Nullable.GetUnderlyingType()được prop.PropertyTypetrả về giá trị không phải null, hãy sử dụng giá trị đó làm kiểu cột. Nếu không, hãy sử dụng prop.PropertyTypechính nó.


4

Tôi biết câu hỏi này đã cũ, nhưng tôi đã gặp vấn đề tương tự đối với một phương pháp mở rộng mà tôi đã thực hiện. Sử dụng phản hồi từ Marc Gravell, tôi có thể sửa đổi mã của mình. Phương thức mở rộng này sẽ xử lý danh sách các kiểu nguyên thủy, chuỗi, kiểu liệt kê và các đối tượng có thuộc tính nguyên thủy.

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

/// <summary>
/// Converts a List&lt;T&gt; to a DataTable.
/// </summary>
/// <typeparam name="T">The type of the list collection.</typeparam>
/// <param name="list">List instance reference.</param>
/// <returns>A DataTable of the converted list collection.</returns>
public static DataTable ToDataTable<T>(this List<T> list)
{
    var entityType = typeof (T);

    // Lists of type System.String and System.Enum (which includes enumerations and structs) must be handled differently 
    // than primitives and custom objects (e.g. an object that is not type System.Object).
    if (entityType == typeof (String))
    {
        var dataTable = new DataTable(entityType.Name);
        dataTable.Columns.Add(entityType.Name);

        // Iterate through each item in the list. There is only one cell, so use index 0 to set the value.
        foreach (T item in list)
        {
            var row = dataTable.NewRow();
            row[0] = item;
            dataTable.Rows.Add(row);
        }

        return dataTable;
    }
    else if (entityType.BaseType == typeof (Enum))
    {
        var dataTable = new DataTable(entityType.Name);
        dataTable.Columns.Add(entityType.Name);

        // Iterate through each item in the list. There is only one cell, so use index 0 to set the value.
        foreach (string namedConstant in Enum.GetNames(entityType))
        {
            var row = dataTable.NewRow();
            row[0] = namedConstant;
            dataTable.Rows.Add(row);
        }

        return dataTable;
    }

    // Check if the type of the list is a primitive type or not. Note that if the type of the list is a custom 
    // object (e.g. an object that is not type System.Object), the underlying type will be null.
    var underlyingType = Nullable.GetUnderlyingType(entityType);
    var primitiveTypes = new List<Type>
    {
        typeof (Byte),
        typeof (Char),
        typeof (Decimal),
        typeof (Double),
        typeof (Int16),
        typeof (Int32),
        typeof (Int64),
        typeof (SByte),
        typeof (Single),
        typeof (UInt16),
        typeof (UInt32),
        typeof (UInt64),
    };

    var typeIsPrimitive = primitiveTypes.Contains(underlyingType);

    // If the type of the list is a primitive, perform a simple conversion.
    // Otherwise, map the object's properties to columns and fill the cells with the properties' values.
    if (typeIsPrimitive)
    {
        var dataTable = new DataTable(underlyingType.Name);
        dataTable.Columns.Add(underlyingType.Name);

        // Iterate through each item in the list. There is only one cell, so use index 0 to set the value.
        foreach (T item in list)
        {
            var row = dataTable.NewRow();
            row[0] = item;
            dataTable.Rows.Add(row);
        }

        return dataTable;
    }
    else
    {
        // TODO:
        // 1. Convert lists of type System.Object to a data table.
        // 2. Handle objects with nested objects (make the column name the name of the object and print "system.object" as the value).

        var dataTable = new DataTable(entityType.Name);
        var propertyDescriptorCollection = TypeDescriptor.GetProperties(entityType);

        // Iterate through each property in the object and add that property name as a new column in the data table.
        foreach (PropertyDescriptor propertyDescriptor in propertyDescriptorCollection)
        {
            // Data tables cannot have nullable columns. The cells can have null values, but the actual columns themselves cannot be nullable.
            // Therefore, if the current property type is nullable, use the underlying type (e.g. if the type is a nullable int, use int).
            var propertyType = Nullable.GetUnderlyingType(propertyDescriptor.PropertyType) ?? propertyDescriptor.PropertyType;
            dataTable.Columns.Add(propertyDescriptor.Name, propertyType);
        }

        // Iterate through each object in the list adn add a new row in the data table.
        // Then iterate through each property in the object and add the property's value to the current cell.
        // Once all properties in the current object have been used, add the row to the data table.
        foreach (T item in list)
        {
            var row = dataTable.NewRow();

            foreach (PropertyDescriptor propertyDescriptor in propertyDescriptorCollection)
            {
                var value = propertyDescriptor.GetValue(item);
                row[propertyDescriptor.Name] = value ?? DBNull.Value;
            }

            dataTable.Rows.Add(row);
        }

        return dataTable;
    }
}

Những công việc này! Nhưng chỉ với các kiểu nullable. Bạn cần thêm câu lệnh "if (underlyingType == null) underlyingType = entityType;" trước "bool typeIsPrimitive = originalTypes.Contains (underlyingType) của bạn;" vì vậy nó cũng hoạt động cho List <int>. Cảm ơn bạn đã chia sẻ!
Krisztián Balla

4

Đây là phiên bản có một số sửa đổi để cho phép các ký tự null và '\ 0' mà không làm nổ DataTable.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Data;

namespace SomeNamespace
{
    public static class Extenders
    {
        public static DataTable ToDataTable<T>(this IEnumerable<T> collection, string tableName)
        {
            DataTable tbl = ToDataTable(collection);
            tbl.TableName = tableName;
            return tbl;
        }

        public static DataTable ToDataTable<T>(this IEnumerable<T> collection)
        {
            DataTable dt = new DataTable();
            Type t = typeof(T);
            PropertyInfo[] pia = t.GetProperties();
            object temp;
            DataRow dr;

            for (int i = 0; i < pia.Length; i++ )
            {
                dt.Columns.Add(pia[i].Name, Nullable.GetUnderlyingType(pia[i].PropertyType) ?? pia[i].PropertyType);
                dt.Columns[i].AllowDBNull = true;
            }

            //Populate the table
            foreach (T item in collection)
            {
                dr = dt.NewRow();
                dr.BeginEdit();

                for (int i = 0; i < pia.Length; i++)
                {
                    temp = pia[i].GetValue(item, null);
                    if (temp == null || (temp.GetType().Name == "Char" && ((char)temp).Equals('\0')))
                    {
                        dr[pia[i].Name] = (object)DBNull.Value;
                    }
                    else
                    {
                        dr[pia[i].Name] = temp;
                    }
                }

                dr.EndEdit();
                dt.Rows.Add(dr);
            }
            return dt;
        }

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