我编写了一个ToList();
扩展方法,将DataTable转换为List。这只适用于某些情况,但我们有很多旧代码使用DataTables,有时需要它。我的问题是,这种方法适用于反射什么是好的,但不是那种高效的。对于100.000 DataRows,我需要1,2sek。
所以我决定用Expression Trees构建它。起初我想替换Setter Call of Properties。到目前为止,我可以轻松获得价值:
var exactType = Nullable.GetUnderlyingType(propType) ?? propType;
var wert = Convert.ChangeType(zeile[spaltenname], exactType);
并设置它:
propertyInfo.SetValue(tempObjekt, wert, null);
现在我搜索了StackOverflow,发现了这个:
var zielExp = Expression.Parameter(typeof(T));
var wertExp = Expression.Parameter(propType);
var propertyExp = Expression.Property(zielExp, matchProp);
var zuweisungExp = Expression.Assign(propertyExp, wertExp);
var setter = Expression.Lambda<Action<T, int>>(zuweisungExp, zielExp, wertExp).Compile();
setter(tempObjekt, wert);
我的大问题是Lambda Action需要一个整数。但我需要这个期待我的财产的类型。我通过PropertyInfo获得了我的房产类型。但是不能让它发挥作用。以为我可以很容易地做出:
Action<T, object>
但这导致以下例外:
ArgumentException Type&#34; System.Int32&#34;中的ParameterExpression; 不能用作Type&#34; System.Object&#34;。
中的Delegateparameter
有人知道可能的解决方案吗?
答案 0 :(得分:1)
您可以使用this overload代替通用的Expression.Lambda
方法,该类型采用以下类型:
public static LambdaExpression Lambda(
Type delegateType,
Expression body,
params ParameterExpression[] parameters
)
然后,您可以使用Type.MakeGenericType
方法为您的操作创建类型:
var actionType = typeof(Action<,>).MakeGenericType(typeof(T), proptype);
var setter = Expression.Lambda(actionType, zuweisungExp, zielExp, wertExp).Compile();
您也可以构建表达式运行时以使用select将DataTable
映射到类型为T
的类,因此只需要使用一次反射,这样可以大大提高性能。我编写了以下扩展方法将DataTable
转换为List<T>
(请注意,如果您不打算将所有数据列映射到属性,此方法将抛出运行时异常在课堂上,所以如果可能的话,请务必注意这一点):
public static class LocalExtensions
{
public static List<T> DataTableToList<T>(this DataTable table) where T : class
{
//Map the properties in a dictionary by name for easy access
var propertiesByName = typeof(T)
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.ToDictionary(p => p.Name);
var columnNames = table.Columns.Cast<DataColumn>().Select(dc => dc.ColumnName);
//The indexer property to access DataRow["columnName"] is called "Item"
var property = typeof(DataRow).GetProperties().First(p => p.Name == "Item"
&& p.GetIndexParameters().Length == 1
&& p.GetIndexParameters()[0].ParameterType == typeof(string));
var paramExpr = Expression.Parameter(typeof(DataRow), "r");
var newExpr = Expression.New(typeof(T));
//Create the expressions to map properties from your class to the corresponding
//value in the datarow. This will throw a runtime exception if your class
//doesn't contain properties for all columnnames!
var memberBindings = columnNames.Select(columnName =>
{
var pi = propertiesByName[columnName];
var indexExpr = Expression.MakeIndex(paramExpr, property,
new[] { Expression.Constant(columnName) });
//Datarow["columnName"] is of type object, cast to the right type
var convert = Expression.Convert(indexExpr, pi.PropertyType);
return Expression.Bind(pi, convert);
});
var initExpr = Expression.MemberInit(newExpr, memberBindings);
var func = Expression.Lambda<Func<DataRow, T>>(initExpr,paramExpr).Compile();
return table.Rows.Cast<DataRow>().Select(func).ToList();
}
}
然后我写了一个小的测试类和一些代码,它们创建了一个可以映射到列表的1,000,000行的数据表。构建表达式+转换为列表现在只需要486毫秒(我认为它是一个非常小的课程):
class Test
{
public string TestString { get; set; }
public int TestInt { get; set; }
}
class Program
{
static void Main()
{
DataTable table = new DataTable();
table.Columns.Add(new DataColumn("TestString", typeof(string)));
table.Columns.Add(new DataColumn("TestInt", typeof(int)));
for(int i = 0; i < 1000000; i++)
{
var row = table.NewRow();
row["TestString"] = $"String number: {i}";
row["TestInt"] = i;
table.Rows.Add(row);
}
var stopwatch = Stopwatch.StartNew();
var myList = table.DataTableToList<Test>();
stopwatch.Stop();
Console.WriteLine(stopwatch.Elapsed.ToString());
}
}
答案 1 :(得分:1)
我想我已经理解你了。我无法翻译你的变量,所以我根据你在问题中看到的内容在这里做出最好的猜测:
对于Action<object,object>
,其中第一个参数是实体本身,第二个参数是您可以使用的属性类型
var instance = Expression.Parameter(typeof(object), "i");
var argument = Expression.Parameter(typeof(object), "a");
var convertObj = Expression.TypeAs(instance, propertyInfo.DeclaringType);
var convert = Expression.Convert(argument, propertyInfo.PropertyType);
var setterCall = Expression.Call(convertObj, propertyInfo.GetSetMethod(), convert);
var compiled = ((Expression<Action<object, object>>) Expression.Lambda(setterCall, instance, argument)).Compile();
如果你知道T
(即实体的类型),你可以这样做:
var instance = Expression.Parameter(typeof(T), "i");
var argument = Expression.Parameter(typeof(object), "a");
var convert = Expression.Convert(argument, propertyInfo.PropertyType);
var setterCall = Expression.Call(instance , propertyInfo.GetSetMethod(), convert);
var compiled = ((Expression<Action<T, object>>) Expression.Lambda(setterCall, instance, argument)).Compile();
答案 2 :(得分:1)
我在这里评论是因为我没有必要的声誉来评论@Alexander Derek的回复
child_inst
为了避免运行时异常,我添加了一个try-catch和.where()
Parent