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