为泛型仓储查询添加多条件Where子句的实现求助
动态构建泛型仓储的查询条件及优化实现
完全可以基于非空输入值,将字符串或列表值作为Where条件动态加入泛型查询,核心是通过表达式树(Expression Tree)动态构建过滤条件,再与原有仓储方法结合。以下是具体实现方案:
1. 构建动态表达式辅助类
先实现一个通用的表达式工具类,用来生成单个过滤条件并合并多个条件:
public static class ExpressionBuilder { // 生成「属性等于指定值」的表达式 public static Expression<Func<T, bool>> Equal<T>(string propertyName, object value) { var parameter = Expression.Parameter(typeof(T), "entity"); var property = Expression.Property(parameter, propertyName); var constant = Expression.Constant(value); var equalExpr = Expression.Equal(property, constant); return Expression.Lambda<Func<T, bool>>(equalExpr, parameter); } // 生成「属性值在指定列表中」的Contains表达式 public static Expression<Func<T, bool>> Contains<T, TValue>(string propertyName, IEnumerable<TValue> values) { var parameter = Expression.Parameter(typeof(T), "entity"); var property = Expression.Property(parameter, propertyName); var valuesConstant = Expression.Constant(values); var containsMethod = typeof(Enumerable) .GetMethod("Contains", new[] { typeof(IEnumerable<TValue>), typeof(TValue) }); var containsExpr = Expression.Call(containsMethod, valuesConstant, property); return Expression.Lambda<Func<T, bool>>(containsExpr, parameter); } // 生成字符串模糊匹配(Contains)的表达式 public static Expression<Func<T, bool>> StringContains<T>(string propertyName, string value) { var parameter = Expression.Parameter(typeof(T), "entity"); var property = Expression.Property(parameter, propertyName); var constant = Expression.Constant(value, typeof(string)); var containsMethod = typeof(string).GetMethod("Contains", new[] { typeof(string) }); var containsExpr = Expression.Call(property, containsMethod, constant); return Expression.Lambda<Func<T, bool>>(containsExpr, parameter); } // 合并多个条件为And逻辑 public static Expression<Func<T, bool>> CombineAnd<T>(params Expression<Func<T, bool>>[] expressions) { if (expressions == null || expressions.Length == 0) return null; var combinedExpr = expressions[0]; for (int i = 1; i < expressions.Length; i++) { var nextExpr = expressions[i]; combinedExpr = Expression.Lambda<Func<T, bool>>( Expression.AndAlso(combinedExpr.Body, Expression.Invoke(nextExpr, combinedExpr.Parameters[0])), combinedExpr.Parameters[0]); } return combinedExpr; } }
2. 优化原有泛型仓储方法
针对你原有的allRecord方法,做如下优化(符合C#编码规范、依赖注入原则):
public class GenericRepository<T> where T : class { private readonly Context _dbContext; // 通过构造函数注入DbContext,避免每次创建新实例,提升性能和可测试性 public GenericRepository(Context dbContext) { _dbContext = dbContext; } public List<T> AllRecords(Expression<Func<T, bool>> filter = null, int skip = 0, int take = int.MaxValue, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, IList<string> includes = null) { IQueryable<T> query = _dbContext.Set<T>(); // 应用过滤条件 if (filter != null) { query = query.Where(filter); } // 批量处理Include关联查询 if (includes != null) { query = includes.Aggregate(query, (currentQuery, includePath) => currentQuery.Include(includePath)); } // 应用排序 if (orderBy != null) { query = orderBy(query); } // 应用分页(Skip(0)不影响结果,简化逻辑) query = query.Skip(skip).Take(take); return query.ToList(); } }
3. 调用示例:基于非空输入构建查询
假设你有一个包含20个输入字段的查询DTO(如SearchParams),只需判断每个字段是否非空,生成对应条件后合并,再传入仓储方法:
// 示例查询参数DTO public class SearchParams { public string ProductName { get; set; } public List<int> CategoryIds { get; set; } public decimal? MinPrice { get; set; } // 剩余17个输入字段... } // 调用仓储的代码 var searchParams = new SearchParams { ProductName = "手机", CategoryIds = new List<int> { 1, 3 }, MinPrice = 1000M }; var conditions = new List<Expression<Func<Product, bool>>>(); // 非空判断后添加对应条件 if (!string.IsNullOrEmpty(searchParams.ProductName)) { conditions.Add(ExpressionBuilder.StringContains<Product>(nameof(Product.Name), searchParams.ProductName)); } if (searchParams.CategoryIds != null && searchParams.CategoryIds.Any()) { conditions.Add(ExpressionBuilder.Contains<Product, int>(nameof(Product.CategoryId), searchParams.CategoryIds)); } if (searchParams.MinPrice.HasValue) { // 构建「价格大于等于最小值」的表达式 var parameter = Expression.Parameter(typeof(Product), "p"); var priceProp = Expression.Property(parameter, nameof(Product.Price)); var minPriceConst = Expression.Constant(searchParams.MinPrice.Value); var greaterExpr = Expression.GreaterThanOrEqual(priceProp, minPriceConst); conditions.Add(Expression.Lambda<Func<Product, bool>>(greaterExpr, parameter)); } // 合并所有条件 var filter = ExpressionBuilder.CombineAnd<Product>(conditions.ToArray()); // 注入仓储实例后调用方法 var productRepo = new GenericRepository<Product>(new Context()); var result = productRepo.AllRecords(filter, skip: 0, take: 20);
注意事项
- 确保传入的
propertyName是实体类T中存在的属性,否则会抛出运行时异常,可添加typeof(T).GetProperty(propertyName)的验证逻辑。 - 可根据需求扩展
ExpressionBuilder,添加大于、小于、不等于等更多比较逻辑的方法。 - 若使用依赖注入容器(如Autofac、Microsoft DI),建议将
GenericRepository<T>和Context注册到容器中,避免手动实例化。
内容的提问来源于stack exchange,提问作者Halil İbrahim AYHAN
相关产品推荐
相关产品推荐

