code

.NET-일반 컬렉션을 DataTable로 변환

codestyles 2020. 10. 21. 08:06
반응형

.NET-일반 컬렉션을 DataTable로 변환


일반 컬렉션 (List)을 DataTable로 변환하려고합니다. 이 작업을 수행하는 데 도움이되는 다음 코드를 찾았습니다.

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

내 문제는 MySimpleClass의 속성 중 하나를 nullable 형식으로 변경하면 다음 오류가 발생한다는 것입니다.

DataSet does not support System.Nullable<>.

내 클래스의 Nullable 속성 / 필드로 어떻게 할 수 있습니까?


그런 다음 아마도를 사용하여 nullable이 아닌 형식으로 들어 올려야 할 Nullable.GetUnderlyingType것이며 아마도 몇 가지 null값을 DbNull.Value...

할당을 다음과 같이 변경하십시오.

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

열을 추가 할 때 :

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

그리고 작동합니다. ( ??는 널 통합 연산자입니다. 널이 아닌 경우 첫 번째 피연산자를 사용하고 그렇지 않으면 두 번째 피연산자가 평가되고 사용됩니다.)


잘. DataSet은 nullable 형식을 지원하지 않기 때문에 속성이 제네릭 형식인지 확인하고 해당 형식의 제네릭 정의를 가져온 다음을 사용하여 인수 (실제 형식)를 가져와야 Nullable.GetUnderlyingType합니다. 값이 null이면 DBNull.ValueDataSet에서 사용 하십시오.


경우 Nullable.GetUnderlyingType()귀하의 주어진 prop.PropertyType그 컬럼의 유형으로 반환을하지 않은 null 값을 사용합니다. 그렇지 않으면 prop.PropertyType자체를 사용하십시오 .


이 질문이 오래되었다는 것을 알고 있지만 내가 만든 확장 방법에 대해 동일한 문제가 있습니다. Marc Gravell의 응답을 사용하여 코드를 수정할 수있었습니다. 이 확장 메서드는 기본 속성이있는 기본 형식, 문자열, 열거 및 개체 목록을 처리합니다.

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

다음은 DataTable을 부 풀리지 않고 null 및 '\ 0'문자를 허용하도록 일부 수정 된 버전입니다.

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

    }
}

참고URL : https://stackoverflow.com/questions/701223/net-convert-generic-collection-to-datatable

반응형