ジェネリックコレクション(リスト)を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のプロパティの1つをnull許容型に変更すると、次のエラーが発生することです。
DataSet does not support System.Nullable<>.
クラスのNullableプロパティ/フィールドでこれを行うにはどうすればよいですか?
次に、おそらくNullable.GetUnderlyingType
を使用して、それらをnull不可のフォームに持ち上げ、おそらくいくつかのnull
値をDbNull.Value
...に変更する必要があります。
割り当てを次のように変更します。
row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
そして、列を追加するとき:
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(
prop.PropertyType) ?? prop.PropertyType);
そしてそれは動作します。 (??
はヌル合体演算子です。非ヌルの場合は第1オペランドを使用し、そうでない場合は第2オペランドが評価されて使用されます)
まあ。 DataSetはnull許容型をサポートしていないため、プロパティがジェネリック型であるかどうかを確認し、その型のジェネリック定義を取得し、おそらくNullable.GetUnderlyingType
を使用して引数(実際の型)を取得する必要があります。値がnullの場合、DataSetでDBNull.Value
を使用します。
Nullable.GetUnderlyingType()
でprop.PropertyType
がnull以外の値を返す場合、それを列の型として使用します。それ以外の場合は、prop.PropertyType
自体を使用します。
これは、DataTableを爆破せずにヌルと「\ 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;
}
}
}
私はこの質問が古いことを知っていますが、私が作成した拡張メソッドについても同じ問題がありました。 Marc Gravellからの応答を使用して、コードを変更できました。この拡張メソッドは、プリミティブプロパティ、プリミティブ型、文字列、列挙、およびオブジェクトのリストを処理します。
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;
}
}