如何基于用户输入构建带OR运算符的动态LINQ查询(EF Core DynamicLinq)
基于Microsoft.EntityFrameworkCore.DynamicLinq实现OR逻辑的动态LINQ查询
现有实现与需求
我使用Microsoft.EntityFrameworkCore.DynamicLinq库,已通过链式调用Where实现了AND逻辑的动态LINQ查询,核心代码如下:
实体与查询类定义
public class FilterAnd { public string Field { get; set; } = string.Empty; public string Value { get; set; } = string.Empty; public Criteria Criteria { get; set; } public string GetLinqQuery() { string query = string.Empty; switch (this.Criteria) { case Criteria.Equal: query = $"{Field} == @0"; break; case Criteria.NotEqual: query = $"{Field} != @0"; break; case Criteria.GreaterThan: query = $"{Field} > @0"; break; case Criteria.LessThan: query = $"{Field} < @0"; break; case Criteria.GreaterThanOrEqual: query = $"{Field} >= @0"; break; case Criteria.LessThanOrEqual: query = $"{Field} <= @0"; break; case Criteria.Like: case Criteria.In: case Criteria.NotLike: case Criteria.NotIn: query = $"!{Field}.Contains(@0)"; break; case Criteria.IsNull: query = $"{Field} == null"; break; case Criteria.IsNotNull: query = $"{Field} != null"; break; } return query; } } public class Search { public List<FilterAnd> FiltersAnd { get; set; } = new List<FilterAnd>(); public List<OrderBy> OrderBy { get; set; } = new List<OrderBy>(); public int Skip { get; set; } = 0; public int Take { get; set; } = 2000; }
AND逻辑查询构建方法
public IQueryable<T> PrepareQuerySearch(Search search) { string delimiter = "§^_^§"; var query = _dbContext.Set<T>().AsQueryable(); foreach (var filter in search.FiltersAnd) { switch (filter.Criteria) { case Criteria.In: case Criteria.NotIn: query = query.Where(filter.GetLinqQuery(), filter.Value.Split(delimiter)); break; default: query = query.Where(filter.GetLinqQuery(), filter.Value); break; } } return query; }
现在需要将search.FiltersAnd替换为search.FiltersOr,实现OR逻辑的动态查询,希望找到比手动构建LINQ Expression更简便的方案。
2023.01.10 更新
目前我尝试通过LINQ Expression实现,但工作量较大,以下是Equal条件的实现示例:
public static IQueryable<T> WhereEqual<T>(this IQueryable<T> source, string propertyName, string propertyValue) { var param = Expression.Parameter(typeof(T)); var prop = typeof(T).GetProperty(propertyName); var condition = Expression.Lambda<Func<T, bool>>( Expression.Equal( Expression.Property(param, propertyName), Expression.Constant(propertyValue, prop.PropertyType) ), param ); return source.Where(condition); }
简便实现OR逻辑的方案
利用Microsoft.EntityFrameworkCore.DynamicLinq的字符串查询特性,将所有OR条件拼接为一个带||运算符的查询语句,一次性传入Where方法即可,无需手动构建Expression。
步骤1:调整实体类
将Search类中的FiltersAnd替换为FiltersOr,同时复用原FilterAnd的结构(或重命名为FilterOr):
public class FilterOr { public string Field { get; set; } = string.Empty; public string Value { get; set; } = string.Empty; public Criteria Criteria { get; set; } // 复用原GetLinqQuery方法逻辑 public string GetLinqQuery() { string query = string.Empty; switch (this.Criteria) { case Criteria.Equal: query = $"{Field} == @0"; break; case Criteria.NotEqual: query = $"{Field} != @0"; break; case Criteria.GreaterThan: query = $"{Field} > @0"; break; case Criteria.LessThan: query = $"{Field} < @0"; break; case Criteria.GreaterThanOrEqual: query = $"{Field} >= @0"; break; case Criteria.LessThanOrEqual: query = $"{Field} <= @0"; break; case Criteria.Like: case Criteria.In: case Criteria.NotLike: case Criteria.NotIn: query = $"!{Field}.Contains(@0)"; break; case Criteria.IsNull: query = $"{Field} == null"; break; case Criteria.IsNotNull: query = $"{Field} != null"; break; } return query; } } public class Search { public List<FilterOr> FiltersOr { get; set; } = new List<FilterOr>(); public List<OrderBy> OrderBy { get; set; } = new List<OrderBy>(); public int Skip { get; set; } = 0; public int Take { get; set; } = 2000; }
步骤2:重构查询构建方法
修改PrepareQuerySearch,收集所有OR条件的查询字符串和参数,拼接成一个完整的OR表达式:
public IQueryable<T> PrepareQuerySearch(Search search) { string delimiter = "§^_^§"; var query = _dbContext.Set<T>().AsQueryable(); if (search.FiltersOr?.Any() != true) return query; var orConditions = new List<string>(); var parameters = new List<object>(); foreach (var filter in search.FiltersOr) { // 替换参数占位符为当前参数索引,避免重复使用@0导致参数混淆 var indexedQuery = filter.GetLinqQuery().Replace("@0", $"@{parameters.Count}"); switch (filter.Criteria) { case Criteria.In: case Criteria.NotIn: parameters.Add(filter.Value.Split(delimiter)); break; default: parameters.Add(filter.Value); break; } orConditions.Add(indexedQuery); } // 用||拼接所有OR条件 var combinedOrQuery = string.Join(" || ", orConditions); query = query.Where(combinedOrQuery, parameters.ToArray()); return query; }
方案优势
- 复用原有
GetLinqQuery方法的逻辑,改动极小 - 无需手动处理Expression的拼接逻辑,代码简洁易维护
- 完全利用DynamicLinq的特性,保持查询的可翻译性(能正确转换为SQL)
内容的提问来源于stack exchange,提问作者Tim Allemann
相关产品推荐
相关产品推荐

