有条件地应用 Where(或在它们之间)LINQ

Apply Where conditionally (OR between them) LINQ

我的 asp.net 项目中有过滤器,并希望将表达式添加到此列表,条件为:

Expression<Func<MyModel, bool>> func;
var list = new List<Expression<Func<MyModel, bool>>>();

我想有条件地应用 Where(或它们之间)。例如:

if(sth){
   func = p => p.a <= b;
   list.Add(func);
}
if (sth else){
   func = p => p.c >= d;
   list.Add(func);
}

var fq = Session.Query<MyModel>();
fq = list.Aggregate(fq, (current, expression) => current.Where(expression));

我该怎么做?

看起来你可以使用扩展方法轻松地做到这一点

public static class EnumerableExtensions
{
    public static IEnumerable<T> ConditionalWhere<T>(this IEnumerable<T> list, 
                                               bool condition, Func<T,bool> predicate)
    {
        if(!condition)
             return list;
        return list.Where(predicate);
    }
}

用法为:

var fq = Session.Query<MyModel>();
var result = fq.ConditionalWhere(sth, p => p.a <= b)
               .ConditionalWhere(sth_else, p => p.c >= d);

您可以构建一个扩展方法来使用 OR 关系合并两个条件表达式,如下所示:

public static class Extensions
{
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
    {
        var parameter = one.Parameters[0];
        var visitor = new ReplaceParameterVisitor(parameter);
        another = (Expression<Func<T, bool>>)visitor.Visit(another);
        var body = Expression.Or(one.Body, another.Body);
        return Expression.Lambda<Func<T, bool>>(body, parameter);
    }
}

class ReplaceParameterVisitor : ExpressionVisitor
{
    public ParameterExpression NewParameter { get; private set; }

    public ReplaceParameterVisitor(ParameterExpression newParameter)
    {
        this.NewParameter = newParameter;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return this.NewParameter;
    }
}

用法和测试代码:

Expression<Func<int, bool>> condition1 = x => x > 8;
Expression<Func<int, bool>> condition2 = y => y < 3;            
var condition = condition1.Or(condition2);
var result = Enumerable
    .Range(1, 10)
    .Where(condition.Compile())
    .ToList();      //1,2,9,10

我编写了一些代码来展示 Predicate<>,同时试图坚持您的程序结构:

using System;
using System.Collections.Generic;
using System.Linq;

namespace SOTests
{
    public class MyModel
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }
    class Program
    {
        private static int ControlId;
        private static string ControlName;

        static void Main(string[] args)
        {
            var idPred = new Predicate<MyModel>(m => m.Id > ControlId);
            var namePred = new Predicate<MyModel>(m => m.Name == ControlName);

            var list = new List<MyModel>();

            if (true) // TODO: do id check?
            {
                list = list.Where(m => idPred.Invoke(m)).ToList();
            }

            if (true) // TODO: do name check?
            {
                list = list.Where(m => namePred.Invoke(m)).ToList();
            }

            //var fq = Session.Query<MyModel>();
            //fq = list;
        }
    }
}

我注释掉了 Session 位,不知道它代表哪种存储抽象(并且代码无法编译)。

代码应该会自行解释并且没有经过测试。

它可以更优雅,但你应该更清楚地说明你的要求是什么。

谢谢大家,我找到了解决方案:OrElse

Expression<Func<MyModel, bool>> OrExpressionFunction(Expression<Func<MyModel, bool>> exp1, Expression<Func<MyModel, bool>> exp2)
    {
        ParameterExpression p = exp1.Parameters.Single();
        return Expression.Lambda<Func<MyModel, bool>>(
            Expression.OrElse(exp1.Body, exp2.Body), p);
    }

然后:

Expression<Func<MyModel, bool>> MyExp = null;
if(sth){
  func = p => p.a <= b;
  MyExp= OrExpressionFunction(func, MyExp);
}
if (sth else){
  func = p => p.c >= d;
  MyExp= OrExpressionFunction(func, MyExp);
}
list.Add(MyExp);