C# 动态递归lambda表达式

C# 动态递归lambda表达式,c#,asp.net,dynamic,lambda,expression,C#,Asp.net,Dynamic,Lambda,Expression,我想创建动态lambda表达式,以便使用一组过滤参数过滤列表。这就是我到目前为止所做的: 表达式是使用下面的方法构建的,其中T是列表的对象类型 public static Expression<Func<T, bool>> GetExpression<T>(IList<DynamicFilter> filters) { if (filters.Count == 0) return null;

我想创建动态lambda表达式,以便使用一组过滤参数过滤列表。这就是我到目前为止所做的:

表达式是使用下面的方法构建的,其中T是列表的对象类型

    public static Expression<Func<T, bool>> GetExpression<T>(IList<DynamicFilter> filters)
    {
        if (filters.Count == 0)
            return null;

        ParameterExpression param = Expression.Parameter(typeof(T), "t");
        Expression exp = null;

        if (filters.Count == 1)
            exp = GetExpression<T>(param, filters[0]);

        [...]

        return Expression.Lambda<Func<T, bool>>(exp, param);
    }

    private static Expression GetExpression<T>(ParameterExpression param, DynamicFilter filter)
    {
        MemberExpression member = Expression.Property(param, filter.PropertyName);
        ConstantExpression constant = Expression.Constant(filter.Value);

        [...]

        return Expression.Call(member, filterMethod, constant);
    }
但是在编译表达式时,我得到了一个
Lambda参数不在范围内的错误。我有点理解我为什么会犯这样的错误,但我不知道如何让它起作用

底线是我需要使表达式构建方法在形成MemberExpression时递归工作。我最终需要获得一个
list=list.Where(deleg.ToList()转换为如下内容
list=list.Where(obj=>obj.Field0.Value=='something').ToList()

我刚刚开始使用表达式,所以在这方面我真的不知道太多,但任何帮助都将不胜感激


谢谢

看看这里描述的ExpressionVisitor:

我正在努力解决这个问题

因此,我可以使用一组过滤参数过滤列表

不是通过使用
ExpressionBuilder
,而是通过使用通用筛选器类

public class Filter<T> where T: class
{
    private readonly Predicate<T> criteria;

    public Filter(Predicate<T> criteria)
    {
        this.criteria = criteria;
    }

    public bool IsSatisfied(T obj)
    {
        return criteria(obj);
    }
}
然后我们需要一个对象列表

var graywand = new Weapon { Name = "Graywand", MaxDamage = 42, Range = 1, Class = Weapon.WeaponClass.Sword };
var scalpel = new Weapon { Name = "Scalpel", MaxDamage = 33, Range = 1, Class = Weapon.WeaponClass.Sword };
var players = new List<Player> {
    new Player { Name = "Fafhrd", Level = 19, Weapon = graywand }, 
    new Player { Name = "Gray Mouser", Level = 19, Weapon = scalpel }, 
    new Player { Name = "Freddy", Level = 9, Weapon = graywand }, 
    new Player { Name = "Mouse", Level = 8, Weapon = scalpel} 
};

只有“Fafhrd”将出现在
highlevelandpowerfulshows
中,
highLevelOrPowerfulSwords
将包含除“鼠标”以外的所有玩家。

您可以为过滤器中的每个元素生成表达式,并使用以下方法将它们组合成一个表达式:

    public static Expression<Func<T, K>> CombineAnd<T, K>(Expression<Func<T, K>> a, Expression<Func<T, K>> b)
    {
        ParameterExpression firstParameter = a.Parameters.First();
        Expression<Func<T, K>> b1 = Expression.Lambda<Func<T, K>>(Expression.Invoke(b, firstParameter), firstParameter);
        return Expression.Lambda<Func<T, K>>(Expression.And(a.Body, b1.Body), firstParameter);
    }
    public static Expression<Func<T, K>> CombineOr<T, K>(Expression<Func<T, K>> a, Expression<Func<T, K>> b)
    {
        ParameterExpression firstParameter = a.Parameters.First();
        Expression<Func<T, K>> b1 = Expression.Lambda<Func<T, K>>(Expression.Invoke(b, firstParameter), firstParameter);
        return Expression.Lambda<Func<T, K>>(Expression.Or(a.Body, b1.Body), firstParameter);
    }
公共静态表达式组合AND(表达式a、表达式b)
{
ParameterExpression firstParameter=a.Parameters.First();
表达式b1=Expression.Lambda(Expression.Invoke(b,firstParameter),firstParameter);
返回表达式.Lambda(表达式.And(a.Body,b1.Body),firstParameter);
}
公共静态表达式组合器(表达式a、表达式b)
{
ParameterExpression firstParameter=a.Parameters.First();
表达式b1=Expression.Lambda(Expression.Invoke(b,firstParameter),firstParameter);
返回表达式.Lambda(表达式.Or(a.Body,b1.Body),firstParameter);
}

我意识到这是一篇相当古老的帖子,但我遇到了完全相同的问题,并在马克·格雷威尔发布的答案中找到了一些相近的东西。我只是稍微修改了一下以满足我的需要,结果如下:

    private Expression GetDeepProperty(Expression parameter, string property)
    {
        var props = property.Split('.');
        var type = parameter.Type;

        var expr = parameter;
        foreach (var prop in props)
        {
            var pi = type.GetProperty(prop);
            expr = Expression.Property(expr, pi);
            type = pi.PropertyType;
        }

        return expr;
    }
实施:

var method = typeof (string).GetMethod("Contains", new Type[] {typeof (string)}, null);
var lambdaParameter = Expression.Parameter(typeof(TEntity), "te");
var filterExpression = Expression.Lambda<Func<TEntity, bool>> (
            filters.Select(filter => Expression.Call(GetDeepProperty(lambdaParameter, filter.Property),
                                                      method,
                                                      Expression.Constant(filter.Value))).
                Where(exp => exp != null).
                Cast<Expression>().
                ToList().
                Aggregate(Expression.Or), lambdaParameter);
var method=typeof(string).GetMethod(“Contains”,新类型[]{typeof(string)},null);
var lambdaParameter=表达式参数(类型(tenty),“te”);
var filtereexpression=Expression.Lambda(
filters.Select(filter=>Expression.Call(GetDeepProperty(lambdapameter,filter.Property)),
方法,,
Expression.Constant(filter.Value)))。
其中(exp=>exp!=null)。
Cast()。
托利斯()。
骨料(表达式或),λ参数);

很抱歉,此解决方案对我不起作用,因为它不是真正的动态解决方案。使用这样的过滤器类迫使我定义许多组合,而我需要使用参数化过滤器集合。我不知道在编译时过滤了哪些字段(简单或复杂),接收了哪些值以及使用了哪些过滤函数。这就是我需要反射和递归构建表达式树的原因。抱歉,我没有意识到您需要在运行时自行构建过滤器。如果不需要非常快,那么创建一些Python或Ruby代码并使用IronPython/IronRuby执行可能会更容易。从这里我看到的,解决方案是将表达式树与和/或操作数链接在一起,但这不是我需要的。然而,由于我对表达式有点陌生,我不太确定,如果我错了,我希望能得到进一步的解释。更清楚地说,这是我正在使用的方法:我不需要组合它们(或者至少我没有问题),我只需要获得完整的属性路径,就像我在第一篇文章中所说的那样。看起来它可以工作,尽管我现在无法测试它。无论如何,我使用微软的动态linq库解决了我的问题,正如这里所建议的:与自定义表达式生成器相结合。
var powerfulSwords = new Filter<Player>(p => p.Weapon.MaxDamage>35);
var highLevels = new Filter<Player>(p => p.Level>15);

var filters = new List<Filter<Player>>();
filters.Add(powerfulSwords);
filters.Add(highLevels);
var highLevelAndPowerfulSwords = players.Where(p => filters.All(filter => filter.IsSatisfied(p)));
var highLevelOrPowerfulSwords = players.Where(p => filters.Any(filter => filter.IsSatisfied(p)));
    public static Expression<Func<T, K>> CombineAnd<T, K>(Expression<Func<T, K>> a, Expression<Func<T, K>> b)
    {
        ParameterExpression firstParameter = a.Parameters.First();
        Expression<Func<T, K>> b1 = Expression.Lambda<Func<T, K>>(Expression.Invoke(b, firstParameter), firstParameter);
        return Expression.Lambda<Func<T, K>>(Expression.And(a.Body, b1.Body), firstParameter);
    }
    public static Expression<Func<T, K>> CombineOr<T, K>(Expression<Func<T, K>> a, Expression<Func<T, K>> b)
    {
        ParameterExpression firstParameter = a.Parameters.First();
        Expression<Func<T, K>> b1 = Expression.Lambda<Func<T, K>>(Expression.Invoke(b, firstParameter), firstParameter);
        return Expression.Lambda<Func<T, K>>(Expression.Or(a.Body, b1.Body), firstParameter);
    }
    private Expression GetDeepProperty(Expression parameter, string property)
    {
        var props = property.Split('.');
        var type = parameter.Type;

        var expr = parameter;
        foreach (var prop in props)
        {
            var pi = type.GetProperty(prop);
            expr = Expression.Property(expr, pi);
            type = pi.PropertyType;
        }

        return expr;
    }
var method = typeof (string).GetMethod("Contains", new Type[] {typeof (string)}, null);
var lambdaParameter = Expression.Parameter(typeof(TEntity), "te");
var filterExpression = Expression.Lambda<Func<TEntity, bool>> (
            filters.Select(filter => Expression.Call(GetDeepProperty(lambdaParameter, filter.Property),
                                                      method,
                                                      Expression.Constant(filter.Value))).
                Where(exp => exp != null).
                Cast<Expression>().
                ToList().
                Aggregate(Expression.Or), lambdaParameter);