C# 动态递归lambda表达式
我想创建动态lambda表达式,以便使用一组过滤参数过滤列表。这就是我到目前为止所做的: 表达式是使用下面的方法构建的,其中T是列表的对象类型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;
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);