You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在EF.Core中构建动态Where过滤器,处理日期范围、数组及分页

实现自定义动态过滤+分页方案

1. 解析过滤参数

先把传入的filter字符串拆分成键值对,根据值的格式区分不同过滤类型:

  • 日期范围:值以{}包裹,逗号分隔两个日期字符串(格式为ddMMyyyyHHmmss)
  • 数组匹配:值以[]包裹,逗号分隔元素
  • 分页参数:直接键值对(PageNumber/PageSize)

示例解析代码:

var filterParts = filter.Split('&')
                       .Select(p => p.Split(':', 2))
                       .ToDictionary(kv => kv[0].Trim(), kv => kv[1].Trim());

// 提取分页参数,默认值兜底
int pageNumber = filterParts.TryGetValue("PageNumber", out var pnStr) && int.TryParse(pnStr, out var pn) ? pn : 1;
int pageSize = filterParts.TryGetValue("PageSize", out var psStr) && int.TryParse(psStr, out var ps) ? ps : 10;

// 遍历处理过滤条件
foreach (var kv in filterParts.Where(kv => !new[] {"PageNumber", "PageSize"}.Contains(kv.Key)))
{
    var propName = kv.Key;
    var value = kv.Value;
    
    if (value.StartsWith('{') && value.EndsWith('}'))
    {
        // 处理日期范围
        var dateSegments = value.Trim('{', '}').Split(',').Select(s => s.Trim()).ToArray();
        var startDate = DateTime.ParseExact(dateSegments[0], "ddMMyyyyHHmmss", CultureInfo.InvariantCulture);
        var endDate = DateTime.ParseExact(dateSegments[1], "ddMMyyyyHHmmss", CultureInfo.InvariantCulture);
        // 后续构建日期过滤表达式
    }
    else if (value.StartsWith('[') && value.EndsWith(']'))
    {
        // 处理数组匹配
        var elements = value.Trim('[', ']').Split(',').Select(s => s.Trim()).ToArray();
        // 根据属性类型转换,比如int类型:
        var targetArray = elements.Select(int.Parse).ToArray();
        // 后续构建Contains表达式
    }
    else
    {
        // 原有等于/模糊/比较过滤,参考动态Where方案实现
    }
}

2. 构建EF Core兼容的动态过滤表达式

用Expression树构建强类型过滤条件,确保能被EF Core转成SQL:

日期范围过滤

构建x => x.日期属性 >= 开始日期 && x.日期属性 <= 结束日期的表达式:

// T为你的EF Core实体类型
var paramExpr = Expression.Parameter(typeof(T), "x");
var propExpr = Expression.Property(paramExpr, propName);

var startConst = Expression.Constant(startDate, typeof(DateTime));
var endConst = Expression.Constant(endDate, typeof(DateTime));

var gtExpr = Expression.GreaterThanOrEqual(propExpr, startConst);
var ltExpr = Expression.LessThanOrEqual(propExpr, endConst);
var dateFilter = Expression.AndAlso(gtExpr, ltExpr);

数组存在匹配(Contains)

构建x => 目标数组.Contains(x.属性)的表达式:

// 以int数组为例,其他类型需调整泛型参数
var arrayConst = Expression.Constant(targetArray, typeof(int[]));
var containsMethod = typeof(Enumerable).GetMethod(
    "Contains", 
    new[] { typeof(IEnumerable<int>), typeof(int) }
);
var containsFilter = Expression.Call(containsMethod, arrayConst, propExpr);

合并所有过滤条件

将多个过滤表达式用AndAlso合并,生成最终的Where lambda:

// 初始化为true表达式,方便后续合并
var trueExpr = Expression.Constant(true, typeof(bool));
var combinedFilter = trueExpr;

// 逐个合并条件(示例:合并日期过滤)
combinedFilter = Expression.AndAlso(combinedFilter, dateFilter);
// 合并数组过滤
combinedFilter = Expression.AndAlso(combinedFilter, containsFilter);

var whereLambda = Expression.Lambda<Func<T, bool>>(combinedFilter, paramExpr);
var query = dbContext.Set<T>().Where(whereLambda);

3. 分页实现

直接用EF Core的Skip和Take处理分页,注意页码从1开始时的偏移计算:

var skipCount = (pageNumber - 1) * pageSize;
var paginatedQuery = query.Skip(skipCount).Take(pageSize);

// 执行查询
var result = await paginatedQuery.ToListAsync();

核心注意点

  • 类型校验:解析参数时必须匹配实体属性的类型,比如日期格式要严格对应,数组元素类型要和属性一致,避免运行时错误
  • 边界处理:校验日期范围的前后顺序(startDate <= endDate),分页参数要限制最小值(pageNumber >=1,pageSize >=1)
  • 性能优化:给过滤字段(日期、数组匹配字段)添加数据库索引,避免全表扫描
  • 错误捕获:解析参数时捕获格式异常,返回明确的错误提示(如“日期格式错误”“数组元素类型不匹配”)

内容的提问来源于stack exchange,提问作者Rich

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 03:16:15