通过表达式树构建表达式列表

问题描述 投票:1回答:3

我有以下基于列名和值构建表达式的方法:

public Func<TSource, bool> SimpleComparison<TSource>(string property, object value)
{
    var type = typeof (TSource);
    var pe = Expression.Parameter(type, "p");
    var propertyReference = Expression.Property(pe, property);
    var constantReference = Expression.Constant(value);
    return Expression.Lambda<Func<TSource, bool>>
        (Expression.Equal(propertyReference, constantReference),
        new[] { pe }).Compile();
}

我正在尝试创建一个表达式列表,然后使用最终的级联/编译表达式查询我的数据源。

我曾尝试使用Expression.Block,但这个概念对我来说很难理解。我也看过Expression.Loop,但不确定是否要在这里使用。

理想情况下,我可以执行以下操作:

var filters = request.Filter.Filters;

IQueryable<MyDTO> myDataSource = context.MyDataSource;

var expressions = null;

foreach (var filter in filters)
{

    expressions.Add(SimpleExpression<MyDTO>(filter.Name, filter.Value));

}

return myDataSource.Where(expressions);

关于如何执行类似操作的任何想法?

c# linq expression-trees
3个回答
4
投票
您想得太多。您根本不需要合并表达式。唯一复杂的部分是实现SimpleComparison,但您已经完成了。好吧,主要是。您应该返回Expression<Func<...>>,而不是Func<...>,因此应该是

public Expression<Func<TSource, bool>> SimpleComparison<TSource>(string property, object value) { // ... return Expression.Lambda<Func<TSource, bool>> (Expression.Equal(propertyReference, constantReference), new[] { pe }); }

一旦有了,就可以通过重复调用Where来链接过滤器,如下所示:

var filters = request.Filter.Filters; IQueryable<MyDTO> query = context.MyDataSource; foreach (var filter in filters) query = query.Where(SimpleComparison<MyDTO>(filter.Name, filter.Value)); return query;


1
投票
您可以将函数更改为,而不是返回已编译的lambda,而不是返回基本表达式。将这些表达式存储在列表中后,就可以使用Linq Aggregate()函数来构建最终谓词。

public Func<TSource, bool> CreatePredicate<TSource>(IEnumerable<Expression> expressions) { var parameter = Expression.Parameter(typeof(TSource), "x"); var body = expressions.Aggregate((e, next) => Expression.AndAlso(e, next)); var predicate = Expression.Lambda<Func<TSource, bool>>(body, parameter).Compile(); return predicate; }

这是假设您希望使用&&将所有语句连接在一起。如果要使用||将它们加入,请使用Expression.OrElse代替Expression.AndAlso。如果混合使用和和或和过滤器,则解决方案将变得更加复杂。

您上面的示例将成为

var filters = request.Filter.Filters; IQueryable<MyDTO> myDataSource = context.MyDataSource; var expressions = new List<Expression>(); foreach (var filter in filters) { expressions.Add(SimpleComparison<MyDTO>(filter.Name, filter.Value)); } var predicate = CreatePredicate<MyDTO>(expressions); return myDataSource.Where(predicate);


0
投票
我最近遇到了您的确切问题,当尝试将多个表达式与&&或||组合在一起时,以下解决方案为我工作

public Expression<Func<TSource, bool>> SimpleComparison<TSource>(List<QueryFilterObject> queryFilterObjects) { //initialize the body expression BinaryExpression bodyExpression = null; BinaryExpression andExpressionBody = null; BinaryExpression orExpressionBody = null; //create parameter expression ParameterExpression parameterExpression = Expression.Parameter(typeof(TSource), "DynamicFilterQuery"); //list of binary expressions to store either the || or && operators List<BinaryExpression> andExpressions = new List<BinaryExpression>(); List<BinaryExpression> orExpressions = new List<BinaryExpression>(); foreach (var queryFilterObject in queryFilterObjects) { //create member property expression var property = Expression.Property(parameterExpression, queryFilterObject.PropertyName); //create the constant expression value var constantExpressionValue = Expression.Constant(queryFilterObject.Value, queryFilterObject.PropertyType); //create the binary expression clause based on the comparison operator BinaryExpression clause = null; if (queryFilterObject.ComparisonOperator == "==") { clause = Expression.Equal(property, constantExpressionValue); } else if (queryFilterObject.ComparisonOperator == "!=") { clause = Expression.NotEqual(property, constantExpressionValue); } else if (queryFilterObject.ComparisonOperator == ">") { clause = Expression.GreaterThan(property, constantExpressionValue); } else if (queryFilterObject.ComparisonOperator == ">=") { clause = Expression.GreaterThan(property, constantExpressionValue); } else if (queryFilterObject.ComparisonOperator == "<") { clause = Expression.LessThan(property, constantExpressionValue); } else if (queryFilterObject.ComparisonOperator == "<=") { clause = Expression.LessThanOrEqual(property, constantExpressionValue); } //you should validate against a null clause.... //assign the item either to the relevant logical comparison expression list if (queryFilterObject.LogicalOperator == "and" || queryFilterObject.LogicalOperator == "&&") { andExpressions.Add(clause); } else if (queryFilterObject.LogicalOperator == "or" || queryFilterObject.LogicalOperator == "||") { orExpressions.Add(clause); } } if (andExpressions.Count > 0) andExpressionBody = andExpressions.Aggregate((e, next) => Expression.And(e, next)); if (orExpressions.Count > 0) orExpressionBody = orExpressions.Aggregate((e, next) => Expression.Or(e, next)); if (andExpressionBody != null && orExpressionBody != null) bodyExpression = Expression.OrElse(andExpressionBody, orExpressionBody); if (andExpressionBody != null && orExpressionBody == null) bodyExpression = andExpressionBody; if (andExpressionBody == null && orExpressionBody != null) bodyExpression = orExpressionBody; if (bodyExpression == null) throw new Exception("Null Expression."); var finalExpression = Expression.Lambda<Func<WorkOrder, bool>>(bodyExpression, parameterExpression); return finalExpression; } public class QueryFilterObject { public string PropertyName { get; set; } public Type PropertyType { get; set; } public object Value { get; set; } public string ComparisonOperator { get; set; } public string LogicalOperator { get; set; } }

就我而言,我返回的是func(Expression<Func<TSource, bool>>)表达式,而不是func本身(Func<TSource, bool>)。这允许我的where子句保留为Iqueryable,否则返回

[Func<TSource, bool>而不是*Expression<Func<TSource, bool>>*会将您的where子句转换为ienumerable。

最后,我就像下面这样称呼我的表情:

IQueryable<MyDTO> myDataSource = context.MyDataSource; var filter = SimpleComparison(queryFilterObjects); if (filter != null) myDataSource = myDataSource.Where(filter); //perfom other operations such as order by return myDataSource.ToList();

© www.soinside.com 2019 - 2024. All rights reserved.