PlTaylor
PlTaylor

Reputation: 7515

How to set a parameter while using Expression<Func<T>>

I have written the following code to handle mapping parameters from my database to my data types (trust me I wish I could use a std. ORM but that isn't doable for so many reasons)

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
    }

When I try to run this I get the following exception

{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)   }

When I debug it my typedOutput lines up with my property type, so I am not sure why it is throwing this exception.

I am calling it with for example

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

Upvotes: 0

Views: 808

Answers (2)

Ivan Stoev
Ivan Stoev

Reputation: 205559

If you want to keep your current method design, you need a way to somehow evaluate the memberExpression.Expression in order to be able to call SetValue method.

So change the line

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

to

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

and then use one of the following implementations:

(A) This will be sufficient if you use only property accessors:

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) This one is more universal, but slower:

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

Upvotes: 1

Olivier Jacot-Descombes
Olivier Jacot-Descombes

Reputation: 112279

The first parameter of SetValue must be an object containing the property whose value you want to set.

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

Now obj.Offset should have the desired value.

So there are two types involved: The type of the object containing the property and the type of the property itself (e.g. int, string etc.).

Therefore your expression should be like this:

Expression<Func<TEntity, object>> mappingProperty

where TEntity is the type of the object and object is the yet unknown type of a property of this object. Unless you know the type of the property in advance, in which case you would have

Expression<Func<TEntity, TProperty>> mappingProperty

You would call it like this:

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

You must change the type like this (unless the selectedValue is already of the correct type):

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

Upvotes: 0

Related Questions