.NET-преобразование универсальной коллекции в DataTable
Я пытаюсь преобразовать общую коллекцию (список) в 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 свойства/поля в моем классе?
5 ответов:
тогда, по-видимому, вам нужно будет поднять их в ненулевую форму, используя
Nullable.GetUnderlyingType
, и, возможно, изменить несколькоnull
значенияDbNull.Value
...измените назначение на:
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
и при добавлении столбцов:
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType( prop.PropertyType) ?? prop.PropertyType);
и это работает. (
??
является оператором слияния null; он использует первый операнд, если он не равен null, иначе второй операнд вычисляется и используется)
хорошо. Поскольку DataSet не поддерживает типы с нулевым значением, вам нужно будет проверить, является ли свойство универсальным типом, получить общее определение этого типа, а затем получить аргумент( который является фактическим типом), используя, возможно,
Nullable.GetUnderlyingType
. Если значение равно null, просто использоватьDBNull.Value
в наборе данных.
Если
Nullable.GetUnderlyingType()
учитывая вашprop.PropertyType
возвращает значение not-null, используйте его в качестве типа столбца. В противном случае, используйте .
Я знаю, что этот вопрос старый, но у меня была такая же проблема для метода расширения, который я сделал. Используя ответ Марка Гравелла, я смог изменить свой код. Этот метод расширения будет обрабатывать списки примитивных типов, строк, перечислений и объектов с примитивными свойствами.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Linq; using System.Text; /// <summary> /// Converts a List<T> 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; } }
вот версия с некоторыми изменениями, позволяющими использовать нули и символы '\0' без раздувания 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(''))) { dr[pia[i].Name] = (object)DBNull.Value; } else { dr[pia[i].Name] = temp; } } dr.EndEdit(); dt.Rows.Add(dr); } return dt; } } }