如何从表达式中提取列表值

时间:2017-09-27 00:15:19

标签: c# .net-core

我需要从一个数组中读取值,该数组作为表达式提供。

我有这个方法

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

并且像这样被称为

//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));

有什么想法吗?我正在考虑编译表达式,但我被卡在“Closure”类上,因为System.Runtime.CompilerServices.Closure是私有的,我无法使用它。

顺便说一下,我正在使用.NET Core 1.0

修改

我需要得到一个像

这样的字符串
email contains ('n1','n2','n3') 

输入参数必须始终是表达式

Expression<Func<TModel, bool>>

这是因为在内部,此方法可以接收任何表达式,如

x => x.SomeProperty == "n1"

在内部,我处理表达式.Body类型,我有一个针对不同用例的实现。

我无法确定如何实现的情况是输入表达式是

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

1 个答案:

答案 0 :(得分:1)

这是ExpressionAnalizer类的完整实现,它将为您提供所需的输出。

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

    }

用法:

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

响应:

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