我正在研究一个可以在IEnumerable上使用的“ToDataTable()”。很多例子,例如:Convert IEnumerable to DataTable。 DataTables对我来说是可取的,因为我来自Foxpro领域,我们习惯于游标。因此,我挖掘DataTables是因为它们相对简单,易于使用,并且可以容纳不错的列元数据。而且我已经有了很多代码可以很好地显示它们(使用排序和过滤网格标题),导出到Excel等等。
有人对我上面链接的帖子做了一个非常好的评论:“如果类型是字符串,这不起作用,因为属性是字符和长度但是get值然后尝试将完整的字符串放入char列。“
问题是,某些东西适合扩展方法调用(如一维字符串数组),但在转换发生时会抛出错误。我编写了一个扩展方法,它将IsValueType()添加到混合中,但问题是对字符串返回false。最后,我添加了两个kludges:一个参数来强制序列中的项被视为一个值,并且如果发现它是“String”类型,则将序列中的项目视为一个值。
是否有更好的方法可以做到这一点,或者由于他们独特的打字和反射结果,弦乐只是一个奇怪的人?下面的代码适用于我能想到的每个IEnumerable,它适用于除字符串之外的所有DataTable列类型的一维数组(好吧,它适用于带有硬编码kludge的字符串,但否则会出错)。对字符串场景进行硬编码并不是什么大不了的事,我只是希望有更优雅的方法来做到这一点。有什么想法吗?
public static DataTable ToDataTable<T>(this IEnumerable<T> items, string tableName = "", bool treatItemAsValue = false)
{
// We want a single extension method that can take in an enumerable sequence (such as a LINQ query)
// and return the result as a DataTable. We want this to be a one stop shop for converting
// various objects into DataTable format, as DataTables are a nice parallel to Foxpro cursors.
if (items == null) { return null; }
Type itemType = typeof(T);
bool typeIsNullable = itemType.IsGenericType && typeof(T).GetGenericTypeDefinition().Equals(typeof(Nullable<>));
string itemTypeName = "";
bool typeIsValue = false;
Type itemUnderlyingType = itemType;
if (typeIsNullable)
{
// Type of enumerable item is nullable, so we need to find its base type.
itemUnderlyingType = Nullable.GetUnderlyingType(itemType);
}
typeIsValue = itemUnderlyingType.IsValueType;
itemTypeName = itemUnderlyingType.Name;
DataTable dt = new DataTable();
DataColumn col = null;
if ((treatItemAsValue) || (itemTypeName == "String"))
{
// We have been asked to treat the item in the sequence as a value, of the items
// in the sequence are strings. Strings are NOT considered a value type in regards
// to IsValueType(), but when item values are assessed, it will be the value
// of the string that tries to pull in.
typeIsValue = true;
}
if (itemTypeName == "DataRow")
{
// Special case. If our enumerable type is DataRow, then we can utilize a more appropriate
// (built-in) extension method to convert enumerable DataRows to a DataTable.
dt = ((IEnumerable<DataRow>)items).CopyToDataTable();
}
else
{
// We must have an enumerable sequence/collection of some other type, possibly anonymous.
// Get properties of the enumerable to add as columns to the data table.
if (typeIsValue)
{
// Our enumerable items are of a value type (e.g. integers in a one-dimensional array).
col = dt.Columns.Add();
col.AllowDBNull = typeIsNullable;
col.ColumnName = itemTypeName;
col.DataType = itemUnderlyingType;
// Now walk through the enumeration and add rows to our data table (single values).
foreach (var item in items)
{
dt.Rows.Add(item);
}
}
else
{
// The type should be something we can walk through the properties of in order to
// generate properly named and typed columns of our DataTable.
PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in props)
{
Type propType = prop.PropertyType;
// Is it a nullable type? Get the underlying type.
if (propType.IsGenericType && propType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
propType = new NullableConverter(propType).UnderlyingType;
}
dt.Columns.Add(prop.Name, propType);
}
// Now walk through the enumeration and add rows to our data table.
foreach (var item in items)
{
var values = new object[props.Length];
for (int i = 0; i < props.Length; i++)
{
values[i] = props[i].GetValue(item, null);
}
dt.Rows.Add(values);
}
}
}
// Give the DataTable a reasonable name.
if (tableName.Length == 0)
{
if (typeof(T).IsAnonymous())
{
// Anonymous types have really goofy names, so there is no use using that as table name.
tableName = "Anonymous";
}
else
{
// This is NOT an anonymous type, so we can use the type name as table name.
tableName = typeof(T).Name;
}
}
return dt;
}
答案 0 :(得分:1)
好吧,没有回复,除了评论几乎错过了这一点,所以我只会发布我最终的结果。最终版本还会考虑没有属性的项目(例如“对象”本身),并且可以更好地处理空项/值:
public static DataTable ToDataTable<T>(this IEnumerable<T> items, string tableName = "", bool treatItemAsValue = false)
{
// We want a single extension method that can take in an enumerable sequence (such as a LINQ query)
// and return the result as a DataTable. We want this to be a one stop shop for converting
// various objects into DataTable format, as DataTables are a nice parallel to Foxpro cursors.
if (items == null) { return null; }
Type itemType = typeof(T);
bool typeIsNullable = itemType.IsGenericType && typeof(T).GetGenericTypeDefinition().Equals(typeof(Nullable<>));
string itemTypeName = "";
bool typeIsValue = false;
Type itemUnderlyingType = itemType;
if (typeIsNullable)
{
// Type of enumerable item is nullable, so we need to find its base type.
itemUnderlyingType = Nullable.GetUnderlyingType(itemType);
}
typeIsValue = itemUnderlyingType.IsValueType;
itemTypeName = itemUnderlyingType.Name;
DataTable dt = new DataTable();
DataColumn col = null;
PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
if ((treatItemAsValue) || (itemTypeName == "String") || (props.Length == 0))
{
// We have been asked to treat the item in the sequence as a value, or the items
// in the sequence is a string which cannot be "flattened" properly by analyzing properties.
// OR, the type has no properties to put on display, so we should just use the item directly.
// (like the base "Object" type).
typeIsValue = true;
}
if (itemTypeName == "DataRow")
{
// Special case. If our enumerable type is DataRow, then we can utilize a more appropriate
// (built-in) extension method to convert enumerable DataRows to a DataTable.
dt = ((IEnumerable<DataRow>)items).CopyToDataTable();
}
else
{
// We must have an enumerable sequence/collection of some other type, possibly anonymous.
// Get properties of the enumerable to add as columns to the data table.
if (typeIsValue)
{
// Our enumerable items are of a value type (e.g. integers in a one-dimensional array).
col = dt.Columns.Add();
// Whether or not the type is nullable, the value might be null (e.g. for type "Object").
col.AllowDBNull = true;
col.ColumnName = itemTypeName;
col.DataType = itemUnderlyingType;
// Now walk through the enumeration and add rows to our data table (single values).
foreach (var item in items)
{
dt.Rows.Add(item);
}
}
else
{
// The type should be something we can walk through the properties of in order to
// generate properly named and typed columns of our DataTable.
foreach (var prop in props)
{
Type propType = prop.PropertyType;
// Is it a nullable type? Get the underlying type.
if (propType.IsGenericType && propType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
propType = new NullableConverter(propType).UnderlyingType;
}
dt.Columns.Add(prop.Name, propType);
}
// Now walk through the enumeration and add rows to our data table.
foreach (var item in items)
{
if (item != null)
{
// Can only add an item as a row if it is not null.
var values = new object[props.Length];
for (int i = 0; i < props.Length; i++)
{
values[i] = props[i].GetValue(item, null);
}
dt.Rows.Add(values);
}
}
}
}
// Give the DataTable a reasonable name.
if (tableName.Length == 0)
{
if (typeof(T).IsAnonymous())
{
// Anonymous types have really goofy names, so there is no use using that as table name.
tableName = "Anonymous";
}
else
{
// This is NOT an anonymous type, so we can use the type name as table name.
tableName = typeof(T).Name;
}
}
return dt;
}
希望有人觉得这很有用......