EF通用分页仓储方法调试:按类型处理条件过滤遇阻
解决方案:EF泛型仓储实现动态过滤(按类型判断是否应用过滤)
你的核心需求是仅当过滤参数符合有效条件时,才将对应的过滤规则应用到EF查询中(比如字符串非空、Guid非Empty等)。下面是针对这个需求的完整实现方案,解决你之前解析Expression时的问题:
关键思路
- 写一个通用方法判断过滤值是否"有效",决定是否应用该过滤条件;
- 解析过滤表达式,提取要比较的值和对应属性类型;
- 在仓储方法中,仅对有效过滤值对应的表达式执行
Where操作。
完整实现代码
1. 辅助方法:判断过滤值是否有效
private bool IsFilterValueValid(object value, Type propertyType) { if (value == null) return false; // 字符串类型:非null且非空/空白时才过滤 if (propertyType == typeof(string)) { return !string.IsNullOrWhiteSpace((string)value); } // Guid类型:不等于Guid.Empty时才过滤 else if (propertyType == typeof(Guid)) { return (Guid)value != Guid.Empty; } // 可空值类型(如int?、DateTime?):有值时才过滤 else if (Nullable.GetUnderlyingType(propertyType) != null) { return ((System.Nullable)value).HasValue; } // 基础值类型(如int、DateTime):默认全部过滤(可根据业务调整) else if (propertyType.IsValueType) { return true; } // 其他引用类型:非null时才过滤 else { return value != null; } }
2. 辅助方法:解析过滤表达式,提取值和属性类型
private (object Value, Type PropertyType) GetFilterInfo(Expression<Func<TEntity, bool>> filter) { // 处理二元表达式(如x => x.Name == "张三"、x => x.Id != Guid.Empty) if (filter.Body is BinaryExpression binaryExpr) { MemberExpression propertyExpr = null; Expression valueExpr = null; // 匹配左属性右值的情况 if (binaryExpr.Left is MemberExpression leftMember) { propertyExpr = leftMember; valueExpr = binaryExpr.Right; } // 兼容右属性左值的特殊情况(如x => "张三" == x.Name) else if (binaryExpr.Right is MemberExpression rightMember) { propertyExpr = rightMember; valueExpr = binaryExpr.Left; } if (propertyExpr != null && valueExpr != null) { var propertyType = propertyExpr.Type; object value; // 处理常量值(如硬编码的字符串、Guid.Empty) if (valueExpr is ConstantExpression constantExpr) { value = constantExpr.Value; } // 处理变量引用(如传入的参数变量) else { var valueFunc = Expression.Lambda(valueExpr).Compile(); value = valueFunc.DynamicInvoke(); } return (value, propertyType); } } // 处理布尔属性直接作为过滤条件的情况(如x => x.IsActive) if (filter.Body is MemberExpression boolMember && boolMember.Type == typeof(bool)) { return (null, typeof(bool)); } // 默认情况:视为有效过滤条件 return (null, typeof(object)); }
3. 修改后的泛型仓储方法
public virtual async Task<PageFilter<TEntity>> GetWithFilterAndPagination( BaseRequestFilter pagination, List<Expression<Func<TEntity, object>>> includes, List<Expression<Func<TEntity, bool>>> filters) { try { using (var dbContext = IntegramillDbContextFactory.Create()) { var query = dbContext.Set<TEntity>().AsQueryable(); // 处理Include关联查询 if (includes != null && includes.Any()) { foreach (var include in includes) { query = query.Include(include); } } // 处理过滤条件:仅应用有效过滤规则 if (filters != null && filters.Any()) { foreach (var filter in filters) { var (filterValue, propertyType) = GetFilterInfo(filter); // 布尔属性直接应用过滤 if (propertyType == typeof(bool)) { query = query.Where(filter); } // 其他类型仅在值有效时应用 else if (IsFilterValueValid(filterValue, propertyType)) { query = query.Where(filter); } } } // 异步获取总条数 var totalCount = await query.CountAsync(); // 处理分页:兼容Offset为null的情况 var skip = pagination.Offset ?? 0; if (skip > 0) query = query.Skip((int)skip); if (pagination.Take > 0) query = query.Take(pagination.Take); // 异步获取数据列表 var data = await query.ToListAsync(); return new PageFilter<TEntity>(data, totalCount); } } catch (Exception e) { Log.Error(e, "<{EventoId}> - Erro ao CONSULTAR " + typeof(TEntity).Name, "RepositoryError"); throw; } }
注意事项
- 该实现支持常见类型的过滤判断,若有特殊类型(如枚举、自定义值类型),可扩展
IsFilterValueValid方法; - 对于复杂过滤表达式(如
x => x.Name.Contains("xxx")),当前逻辑会直接视为有效条件应用,若需针对这类表达式做判断,可扩展GetFilterInfo的解析逻辑; - 所有EF操作均使用异步API(
CountAsync、ToListAsync),符合异步方法的设计规范; - 处理了
Offset为null的边界情况,默认跳过0条数据。
内容的提问来源于stack exchange,提问作者Herik Sarmento
相关产品推荐
相关产品推荐

