如何在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
相关产品推荐
相关产品推荐

