如何使用多个'Where'表达式,并使用C#/NET将它们与AND和OR链接在一起?

如何解决如何使用多个'Where'表达式,并使用C#/NET将它们与AND和OR链接在一起?

我正在尝试在Web应用程序中创建一个过滤系统。问题是我不知道从客户端向API请求多少个过滤器。我已经构建好了,所以过滤器的数组来自像这样的单个字符串:?sizeFilters=big,small,medium

然后我使用string[] names = sizeFilters.Split(',');来获得像Where(x => x.listOfSizes.contains(names[index]));这样的单个表达式

我还需要使用AND和OR制作表达式的链,因为我将使用另一个过滤器,例如:'?typeFilters=normal,extra,spicy'

因此,我需要使整个表达式看起来像这样,但可能要长几倍,它需要使用不同大小的数组:

退回物品Where size is big OR small OR medium AND Where type is normal OR extra OR spicy

Where(x => x.Sizes == "Small" || x => x.Sizes == "Medium" || x => x.Sizes == "Big" && 
x => x.Types == "normal" || x => x.Types == "extra" || x => x.Types == "Spicy")

解决方法

我认为关注应该有效

        var query = _context.Set<[Entity]>();
        if (sizeFilterPresent)
        {
            query = query.Where(r => sizes.Contains(r.Size));
        }

        if(typesFilterPresent)
        {
          query = query.Where(r => types.Contains(r.Type));
        }
        var results = query.ToList();
,

您可以尝试一下

var result = data.Where(p => sizeFilters.Contains(p.Size) && typeFilters.Contains(p.Type));
,

您可以简单地多次调用.Where一起与AND表达式。动态地对表达式进行“或”运算要困难得多。您需要重新构建表达式图以包含OrElse运算符,并确保所有表达式都基于相同的ParameterExpression

public class Replacer : ExpressionVisitor
{
    private readonly Dictionary<Expression,Expression> _replacements;

    public Replacer(IEnumerable<Expression> before,IEnumerable<Expression> after)
    {
        _replacements = new Dictionary<Expression,Expression>(before.Zip(after,(a,b) => KeyValuePair.Create(a,b)));
    }

    public override Expression Visit(Expression node)
    {
        if (node != null && _replacements.TryGetValue(node,out var replace))
            return base.Visit(replace);
        return base.Visit(node);
    }
}

public static Expression<Func<T,bool>> Or<T>(this Expression<Func<T,bool>> expr1,Expression<Func<T,bool>> expr2)
{
    if (expr1 == null)
        return expr2;
    if (expr2 == null)
        return expr1;
    return Expression.Lambda<Func<T,bool>>(
        Expression.OrElse(
            expr1.Body,new Replacer(expr2.Parameters,expr1.Parameters).Visit(expr2.Body)
        ),expr1.Parameters);
}

public static Expression<Func<T,bool>> And<T>(this Expression<Func<T,bool>>(
        Expression.AndAlso(
            expr1.Body,expr1.Parameters);
}

// Usage
Expression<Func<TableObject,bool>> where = null;
if (...)
    where = where.Or(x => sizeFilters.Contains(x.Size));
if (...)
    where = where.Or(x => typeFilters.Contains(x.Type));
if (where!=null)
    query = query.Where(where);
,

为使外观整洁,我的建议是创建和扩展方法。这样,您可以将其用作任何其他LINQ方法。参见extension methods demystified

假设您的来源是IQuertyable<TSource>

public static IQueryable<TSource> WhereAnd<TSource>(
    this IQueryable<TSource> source,IEnumerable<Expression<Func<TSource,bool>>> filterPredicates)
{
    // TODO: handle null source,expressions;
    IQueryable<TSource> filteredSource = source;
    foreach (var predicate in filterPredicates)
    {
        filteredSource = filteredSource.Where(predicate);
    }
}

用法:

var predicates = new List<Expression<Func<TSource,bool>>>()
{
    customer => customer.BirthDay.Year <= 1950,customer => customer.CityId == GetCityId("New York"),customer => customer.Gender == Gender.Male,}

var oldNewYorkMaleCustomers = dbContext.Customers.WhereAnd(predicates).ToList();

注意:空的filterpredicate集合将不过滤任何谓词:您将获得原始数据:

var emptyFilter = Queryable.Empty<Expression<Func<Customer,bool>>>();
var allCustomers = dbContext.Customers.WhereAnd(emptyFilter);
,

最简单的选择是,如其他人所述,在表达式中使用OR构建Enumerable.Contains。并通过多次调用AND来构建Where

// using these values as an example
string[] sizeTerms = /* initialize */;
string[] typeTerms = /* initialize */;

IQueryable<Item> items = /* initialize */
if (sizeTerms.Any()) {
    items = items.Where(x => sizeTerms.Contains(x.Size));
}
if (typeTerms.Any()) {
    items = items.Where(x => typeTerms.Contains(x.Type));
}

如果需要,可以将此逻辑包装到扩展方法中,该方法接受要过滤的表达式,并使用IEnumerable<string>过滤值;并构造并应用Contains方法:

// using System.Reflection
// using static System.Linq.Expressions.Expression

private static MethodInfo containsMethod = typeof(List<>).GetMethod("Contains");

public static IQueryable<TElement> WhereValues<TElement,TFilterTarget>(
        this IQueryable<TElement> qry,Expression<Func<TElement,TFilterTarget>> targetExpr,IEnumerable<string> values
) {
    var lst = values.ToList();
    if (!lst.Any()) { return qry; }

    return qry.Where(
        Lambda<Expression<Func<TElement,bool>>>(
            Call(
                Constant(lst),containsMethod.MakeGenericMethod(typeof(T)),targetExpr.Body
            ),targetExpr.Parameters.ToArray()
        )
    );
}

可以这样称呼:

qry = qry
    .WhereValues(x => x.Size,sizeTerms)
    .WhereValues(x => x.Type,typeTerms);

一个警告:查询将基于传递给方法的值进行构建;如果以后进行更改,查询将不会反映这些更改。如果这是一个问题:

  • 获取Enumerable.Contains而不是List.Contains的适当重载,并且
  • 使用Expression.Call的重载来生成静态方法调用,而不是实例方法调用。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-