有许多解决方案可以将列表转换为使用反射的DataTable
,这可用于转换匿名类型。但是,它有很多的匿名类型列表,那么性能可能是一个问题。
这是从列表中创建DataTable
的唯一方法吗?有更快的方法吗?
答案 0 :(得分:6)
使用适当的命名POCO / DTO / etc类完成此操作绝对会更好,但它仍然可以完成。可以通过使用元编程来消除反射成本,理想情况是使用预先滚动的库,例如FastMember,如下所示。
请注意,使用匿名类型会强制在此处使用IList
(而不是IList<T>
或List<T>
等)。使用命名类型时,最好使用通用版本。这将允许一些更改 - 特别是itemType
将typeof(T)
,并且即使对于空表也可以创建正确的列。也许更重要的是,它会强制列表是同质的,而不是必须对此做出假设。
using FastMember;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
static class Program
{
static void Main()
{
var list = GetList();
var table = ToTable(list);
}
static DataTable ToTable(IList source)
{
if (source == null) throw new ArgumentNullException();
var table = new DataTable();
if (source.Count == 0) return table;
// blatently assume the list is homogeneous
Type itemType = source[0].GetType();
table.TableName = itemType.Name;
List<string> names = new List<string>();
foreach (var prop in itemType.GetProperties())
{
if (prop.CanRead && prop.GetIndexParameters().Length == 0)
{
names.Add(prop.Name);
table.Columns.Add(prop.Name, prop.PropertyType);
}
}
names.TrimExcess();
var accessor = TypeAccessor.Create(itemType);
object[] values = new object[names.Count];
foreach (var row in source)
{
for (int i = 0; i < values.Length; i++)
{
values[i] = accessor[row, names[i]];
}
table.Rows.Add(values);
}
return table;
}
static IList GetList()
{
return new[] {
new { foo = "abc", bar = 123},
new { foo = "def", bar = 456},
new { foo = "ghi", bar = 789},
};
}
}
答案 1 :(得分:2)
改进@MarcGravell's支持答案:
可以为空的类型。
static public DataTable ToDataTable(this IList anonymousSource, List<string> keepOrderedFieldsOpt = null)
{
// https://stackoverflow.com/a/13153479/538763 - @MarcGravell
// Added keepOrderedFieldsOpt, nullable types - @crokusek
if (anonymousSource == null) throw new ArgumentNullException();
DataTable table = new DataTable();
if (anonymousSource.Count == 0) return table;
// blatently assume the list is homogeneous
Type itemType = anonymousSource[0].GetType();
table.TableName = itemType.Name;
// Build up orderedColumns
//
List<PropertyInfo> orderedColumns;
if (keepOrderedFieldsOpt != null)
{
Dictionary<string, PropertyInfo> propertiesByName = itemType.GetProperties()
.ToDictionary(p => p.Name, p => p);
orderedColumns = new List<PropertyInfo>();
List<string> missingFields = null;
foreach (string field in keepOrderedFieldsOpt)
{
PropertyInfo tempPropertyInfo;
if (propertiesByName.TryGetValue(field, out tempPropertyInfo))
orderedColumns.Add(tempPropertyInfo);
else
(missingFields ?? (missingFields = new List<string>())).Add(field);
}
if (missingFields != null)
throw new ArgumentOutOfRangeException("keepOrderedFieldsOpt", "Argument keepOrderedFieldsOpt contains invalid field name(s): " + String.Join(", ", missingFields));
}
else
orderedColumns = itemType.GetProperties().ToList();
List<string> names = new List<string>();
foreach (PropertyInfo prop in orderedColumns)
{
if (prop.CanRead && prop.GetIndexParameters().Length == 0)
{
names.Add(prop.Name);
// Nullable support from stackoverflow.com/a/23233413/538763 - @Damith
table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
}
}
names.TrimExcess();
TypeAccessor accessor = TypeAccessor.Create(itemType);
object[] values = new object[names.Count];
foreach (var row in anonymousSource)
{
for (int i = 0; i < values.Length; i++)
values[i] = accessor[row, names[i]];
table.Rows.Add(values);
}
return table;
}