Junior
Junior

Reputation: 11990

How can I pass multiple lambda expression to a method then obtain the property info for each expression?

In a C# project, I want to create a function that allows me to pass lambda expression which I can parse each expression into a PropertyInfo where I can extract the property name and the property value.

Here is a stripped down version of my code

public IEnumerable<Student> Make(IEnumerable<User> users, Expression<Func<User, dynamic>> primaryProperty, params Expression<Func<User, dynamic>>[] otherProperties)
{
    var students = new List<Student>();

    foreach(User user in users)
    {
        var student = new Student();
        var mainProp = GetPropertyInfo(user, primaryProperty);
        object mainValue = prop.GetValue(user, null);
        // Do somthing with mainProp.Name...
        // Do something with mainValue ...

        foreach(Expression<Func<User, dynamic> exp in otherProperties ?? new Expression<Func<User, dynamic>>[] {})
        {
            var prop = GetPropertyInfo(user, exp);
            object value = prop.GetValue(user, null);
            // Set the property student property
            // Do somthing with prop.Name...
            // Do something with value...
        }

        students.Add(student);
    }

    return strudents;
}


private static PropertyInfo GetPropertyInfo<TSource, TProperty>(TSource source, Expression<Func<TSource, TProperty>> propertyLambda)
{
    Type type = typeof(TSource);

    if (!(propertyLambda.Body is MemberExpression expression))
    {
        throw new ArgumentException($"Expression '{propertyLambda}' refers to a method, not a property.");
    }

    PropertyInfo propInfo = expression.Member as PropertyInfo;

    if (propInfo == null)
    {
        throw new ArgumentException($"Expression '{propertyLambda}' refers to a field, not a property.");
    }

    if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType))
    {
        throw new ArgumentException($"Expression '{propertyLambda}' refers to a property that is not from type {type}.");
    }

    return propInfo;
}

When I pass property of a primitive type to the function, the GetPropertyInfo fails as propertyLambda.Body as MemberExpression expression returns null.

From google, it seems that the cause of this issue is because I am using dynamic as a return value to the function where it should be something like TProperty instead. Here is a reference Expression.Body as MemberExpression returns null for primitive property

However, I am not sure how to rewrite my Make method to use TProperty instead of dynamic every property can have a different type.

Question How can I pass multiple lambda expression to the Make method then obtain the property info for each expression?

Upvotes: 1

Views: 159

Answers (1)

StriplingWarrior
StriplingWarrior

Reputation: 156524

You should be fine to use object instead of dynamic in your expression types.

public IEnumerable<Student> Make(IEnumerable<User> users, Expression<Func<User, object>> primaryProperty, params Expression<Func<User, object>>[] otherProperties)

The thing to be aware of is that your expression body will most likely be wrapped in a Convert expression, representing the fact that your property is being implicitly cast as an object. So you'll probably need code something like this in your GetPropertyInfo method.

var expressionBody = propertyLambda.Body;
if (expressionBody is UnaryExpression expression && expression.NodeType == ExpressionType.Convert)
{
    expressionBody = expression.Operand;
}

Upvotes: 1

Related Questions