C#使用ExpressionTree将数据表映射到列表<;T>;

C#使用ExpressionTree将数据表映射到列表<;T>;,c#,lambda,datatable,expression,C#,Lambda,Datatable,Expression,我写了一个ToList()将数据表转换为列表的扩展方法。这只在某些情况下有效,但我们有很多使用数据表的旧代码,有时需要它。我的问题是,这种方法可以与反射一起工作,但没有那么好的性能。我需要大约1,2sek来存储100000个数据行 所以我决定用表达式树来构建它。首先,我想替换属性的Setter调用。到目前为止,我可以轻松获得以下值: var exactType = Nullable.GetUnderlyingType(propType) ?? propType; var wert = Conve

我写了一个
ToList()将数据表转换为列表的扩展方法。这只在某些情况下有效,但我们有很多使用数据表的旧代码,有时需要它。我的问题是,这种方法可以与反射一起工作,但没有那么好的性能。我需要大约1,2sek来存储100000个数据行

所以我决定用表达式树来构建它。首先,我想替换属性的Setter调用。到目前为止,我可以轻松获得以下值:

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);
var zielExp=Expression.Parameter(typeof(T));
var wertExp=表达式.参数(propType);
var propertyExp=Expression.Property(zielExp,matchProp);
var zuweisungExp=Expression.Assign(propertyExp,wertExp);
var setter=Expression.Lambda(zuweisungExp、zielExp、wertExp).Compile();
二传手(tempObjekt,wert);
我的大问题是Lambda操作需要一个整数。但我需要这个期望我的财产类型。我通过PropertyInfo获得了我的财产类型。但我不能让它工作。我想我可以很容易地做到:

Action<T, object>
动作
但这会导致以下例外情况:

ArgumentException类型“System.Int32”中的ParameterExpression 不能用作类型“System.Object”中的Delegateparameter


有人知道一个可能的解决方案?

而不是通用的
表达式。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();
然后,我编写了一个小的testclass和一些代码,用于创建一个包含1000000行的datatable,并将其映射到一个列表。在我的电脑上构建表达式+转换为列表现在只需要486ms(当然这是一个非常小的类):

类测试
{
公共字符串TestString{get;set;}
公共int测试{get;set;}
}
班级计划
{
静态void Main()
{
DataTable=新的DataTable();
table.Columns.Add(新数据列(“TestString”,typeof(string)));
表.Columns.Add(新数据列(“TestInt”,typeof(int));
对于(int i=0;i<1000000;i++)
{
var row=table.NewRow();
行[“TestString”]=$“字符串编号:{i}”;
行[“TestInt”]=i;
table.Rows.Add(行);
}
var stopwatch=stopwatch.StartNew();
var myList=table.DataTableToList();
秒表;
Console.WriteLine(stopwatch.appeased.ToString());
}
}

我想我对你的理解是正确的。我无法翻译你的变量,因此我根据你问题中看到的情况,在这里进行最佳猜测:

对于
操作
,其中第一个参数是实体本身,第二个参数是可以使用的属性类型

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();

我在这里发表评论是因为我没有必要的声誉来评论@Alexander Derek的回应

    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);
    });
为了避免运行时异常,我添加了一个try-catch和.where()


谢谢,这看起来很不错。但如果我试图调用setter know,编译器会告诉我“需要方法或委托”。我正在尝试这个setter(myTempObject,wert)@Sebi您需要调用
setter.DynamicInvoke(…)
才能使用它,因为这是可行的。加载时间现在是80sek:D,但这是一个问题。我得到了修复。@Sebi
DynamicInvoke
需要使用大量反射,所以我可以想象,如果在一个紧密的循环中调用它,它会很慢;-)但我仍然不完全理解您想要做什么,您想要一个泛型方法将datatable映射到现有类的列表吗?@Sebi编辑了我的答案,我认为这就是您想要的(反射用于构建表达式,而不是在循环中)那么您正试图根据传递给对象的值调用对象的setter?属性的类型是否为int?我有点困惑。无需动态调用,您可以执行任何操作。但为了提供帮助,我需要知道它是否是单个属性、所有属性、属性列表等。您可以使用一个操作,获取属性的GetSetMethod()的方法信息您可以将类型作为泛型参数传递给它,或者因为您不知道它,所以使用object并调用Expression.Convert。问题是每个解决方案都需要使用反射。因此,这并不比调用propertyInfo.SetValue()更快;如果缓存lambda并只编译它一次,它应该会更快。你不应该在每个循环中调用这个。你的另一个选择是像上面一样使用expression.property和assign,但是你仍然需要转换到正确的类型,因为你仍然需要知道类型,你需要属性infoYes,我构建了一个版本来缓存整个内容,但是仍然比反射访问慢2倍。所以我觉得用这个对我来说更好。谢谢你的帮助。上面的答案已经更新,他的解决方案现在应该对你很有效。我在代码中使用了类似的东西来将数据读取器记录映射到objectOh perfect,我将尝试一下:)
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();
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();
    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 memberBindings = columnNames.Select(columnName =>
        {
            try
            {
                var pi = propertiesByName[columnName];
                var indexExpr = Expression.MakeIndex(paramExpr, property,
                    new[] { Expression.Constant(columnName) });
                var convert = Expression.Convert(indexExpr, pi.PropertyType);
                return Expression.Bind(pi, convert);
            }
            catch(Exception e)
            {
                return null;
            }                
        });
        var initExpr = Expression.MemberInit(newExpr, memberBindings.Where(obj => obj != null));