如何在使用Expression <func <t>&gt;时设置参数

时间:2016-07-08 20:25:22

标签: c# generics expression-trees

我编写了以下代码来处理从我的数据库到我的数据类型的映射参数(相信我,我希望我可以使用std.ORM,但由于这么多原因,这是不可行的)

public void LoadDatabaseValue<T>(DataTable partData, string identifier, string mappingName, Expression<Func<T>> mappingProperty)
    {
        var partAttributeValue = mappingProperty.Name;
        var memberExpression = (MemberExpression)mappingProperty.Body;
        var prop = (PropertyInfo)memberExpression.Member;
        try
        {
            var selectedRow = partData.Select($"partattributename = '{mappingName}'");
            var selectedValue = selectedRow[0]["PartAttributeValue"];

            var typedOutput = (T)Convert.ChangeType(selectedValue, typeof(T));

            prop.SetValue(memberExpression.Expression, typedOutput, null);
        }
        catch (Exception exception)
        {
            _databaseImportError = true;
            // code to log this error
    }

当我尝试运行此操作时,我收到以下异常

{System.Reflection.TargetException: Object does not match target type.
at System.Reflection.RuntimeMethodInfo.CheckConsistency(Object target)
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj,   BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, BindingFlags invokeAttr, Binder binder, Object[] index, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)   }

当我调试它时,我的typedOutput与我的属性类型对齐,所以我不确定它为什么抛出这个异常。

我用它来称呼它

LoadDatabaseValue(partData, identifier, "Offset", () => Offset);

2 个答案:

答案 0 :(得分:1)

如果您想保留当前的方法设计,您需要一种方法来以某种方式评估memberExpression.Expression,以便能够调用SetValue方法。

所以改变行

prop.SetValue(memberExpression.Expression, typedOutput, null);

prop.SetValue(Evaluate(memberExpression.Expression), typedOutput, null);

然后使用以下实现之一:

(A)如果您只使用属性访问器,这就足够了:

static object Evaluate(Expression e)
{
    if (e == null) return null;
    var me = e as MemberExpression;
    if (me != null)
        return ((PropertyInfo)me.Member).GetValue(Evaluate(me.Expression), null);
    return ((ConstantExpression)e).Value;
}

(B)这个更普遍,但更慢:

static object Evaluate(Expression e)
{
    if (e == null) return null;
    return Expression.Lambda(e).Compile().DynamicInvoke();
}

答案 1 :(得分:0)

SetValue的第一个参数必须是包含要设置其值的属性的对象。

var obj = new TEntity();
prop.SetValue(obj, typedOutput); // From .NET 4.5 there is an overload with just 2 parameters

现在obj.Offset应该具有所需的值。

因此涉及两种类型:包含属性的对象的类型和属性本身的类型(例如intstring等。)

因此你的表达应该是这样的:

Expression<Func<TEntity, object>> mappingProperty

其中TEntity是对象的类型,object是此对象的未知类型的属性。除非您事先知道房产的类型,否则您将拥有

Expression<Func<TEntity, TProperty>> mappingProperty

你会这样称呼:

LoadDatabaseValue(partData, identifier, "Offset", x => x.Offset);

您必须更改此类型(除非selectedValue的类型正确):

object typedOutput = Convert.ChangeType(selectedValue, prop.PropertyType);