How extract list values from expression

Viewed 487

I need to read the values from one array, that is provided as an expression.

I have this method

public ExpressionAnalizer<TModel> where TModel : class 
{
    public string BuildExpression(Expression<Func<TModel, bool>> expression)
    {   
        if (expression?.Body is MethodCallExpression)
            return BuildMethodCallExpression(expression);                

        throw new ArgumentException($"The expression '{expression?.Body}' is unsupported");
    }

    public string BuildMethodCallExpression(Expression<Func<TModel, bool>> expression)
    {
        var body = expression.Body as MethodCallExpression;
        //TODO: I can't find a property that has the values of IEnumerable
        return null;
    }
}

And is called like this

//PersonModel is a plain class with some properties.
var analizer= new ExpressionAnalizer<PersonModel>("");
var names = new List<string>() {"n1", "n2", "n3"};
//I want to get "Email contains ('n1', 'n2', 'n3')". I can read Email property, the call method name "Contains", but not itself values
var response = analizer.BuildExpression(x => names.Contains(x.Email));

Any idea? I was thinking to compile the expression, but I get stuck on "Closure" class, because System.Runtime.CompilerServices.Closure is private and I cannot use it.

By the way, I'm using .NET Core 1.0

EDIT

I need to get a string like

email contains ('n1','n2','n3') 

And the input parameter need to be always an Expression

Expression<Func<TModel, bool>>

This is because internally, this method can receive any expression, like

x => x.SomeProperty == "n1"

Internally, I handle the expression.Body type and I have an implementation for different use cases.

The case I cannot figure how can I implement is when the input expression is

var someList = new List<string>() { "string1", "anotherString", "finalString" };
someObject.SomeProperty<SomeTModel>(x => someList.Contains(x.SomeProperty))
1 Answers

Here is the full implementation of your ExpressionAnalizer class which will give you the desired output.

public class ExpressionAnalizer<TModel> where TModel : class
    {
        public string BuildExpression(Expression<Func<TModel, bool>> expression)
        {
            if (expression?.Body is MethodCallExpression)
                return BuildMethodCallExpression(expression);

            throw new ArgumentException($"The expression '{expression?.Body}' is unsupported");
        }

        public string BuildMethodCallExpression(Expression<Func<TModel, bool>> expression)
        {
            var body = expression.Body as MethodCallExpression;

            //Get Method Name
            string method = body.Method.Name;

            //Get List of String Values 
            var methodExpression = ResolveMemberExpression(body.Object);
            var listValues = ReadValue(methodExpression);
            var vString = string.Format("'{0}'", string.Join("' , '", (listValues as List<string>)));

            //Read Propery Name
            var argExpression = ResolveMemberExpression(body.Arguments[0]);
            var propertyName = argExpression.Member.Name;

            return $"{propertyName} {method} ({vString})";  

        }

        public MemberExpression ResolveMemberExpression(Expression expression)
        {

            if (expression is MemberExpression) return (MemberExpression)expression;
            if (expression is UnaryExpression) return (MemberExpression)((UnaryExpression)expression).Operand;

            throw new NotSupportedException(expression.ToString());
        }

        private object ReadValue(MemberExpression expression)
        {
            if (expression.Expression is ConstantExpression)
            {
                return (((ConstantExpression)expression.Expression).Value)
                        .GetType()
                        .GetField(expression.Member.Name)
                        .GetValue(((ConstantExpression)expression.Expression).Value);
            }
            if (expression.Expression is MemberExpression) return ReadValue((MemberExpression)expression.Expression);

            throw new NotSupportedException(expression.ToString());
        }

    }

Usage:

var analizer= new ExpressionAnalizer<PersonModel>();
var names = new List<string>() {"n1", "n2", "n3"};
var person = new PersonModel{ Email = "Email 1"};
var response = analizer.BuildExpression( x => names.Contains(x.Email));

Response:

Email Contains ('n1' , 'n2' , 'n3')
Related