如何在LINQ中针对多个可空Int键实现多条件筛选?
基于多可空Int键的数据查询优化需求
问题背景
需要编写一个查询,基于多个可空Int类型的键检索数据,使用的筛选参数DTO如下:
public class FilterParamsDto { public int? StudentId { get; set; } public int? NationalityId { get; set; } public int? CountryId { get; set; } public int? SchoolCountryId { get; set; } public int? SchoolStateId { get; set; } public int? SchoolCityId { get; set; } // 更多属性... }
最初尝试的查询逻辑嵌套复杂,难以维护:
var value = from y in data where dto.CountryId == null ? y.CountryId != null : y.IntakeId == dto.CountryId && dto.StudentId == null ? y.StudentId != null : y.StudentId == dto.StudentId && dto.SchoolCityId == null ? y.SchoolCityId != null : y.SchoolCityId == dto.SchoolCityId select y;
核心需求
- 当DTO的任意属性有值且值不为0时,基于该属性筛选数据;若属性无值或值为0,则跳过该条件
- 筛选逻辑为链式执行:用第一个有效条件过滤数据集后,再用下一个有效条件过滤上一步的结果,依次类推
- 覆盖三种场景:所有属性有值、部分属性有值、所有属性无值/值为0(此时返回全部数据)
解决方案
方案1:链式Where查询(直观易维护)
这种方式逻辑清晰,完全符合链式筛选的要求,ORM框架(如EF Core)会自动将多个Where条件合并为单一SQL查询,不会产生性能损耗。
// 初始化为可查询数据集 var query = data.AsQueryable(); // 逐个处理筛选属性,仅当属性有值且不为0时添加筛选条件 if (dto.StudentId.HasValue && dto.StudentId.Value != 0) { query = query.Where(y => y.StudentId == dto.StudentId.Value); } if (dto.NationalityId.HasValue && dto.NationalityId.Value != 0) { query = query.Where(y => y.NationalityId == dto.NationalityId.Value); } if (dto.CountryId.HasValue && dto.CountryId.Value != 0) { query = query.Where(y => y.CountryId == dto.CountryId.Value); } if (dto.SchoolCountryId.HasValue && dto.SchoolCountryId.Value != 0) { query = query.Where(y => y.SchoolCountryId == dto.SchoolCountryId.Value); } if (dto.SchoolStateId.HasValue && dto.SchoolStateId.Value != 0) { query = query.Where(y => y.SchoolStateId == dto.SchoolStateId.Value); } if (dto.SchoolCityId.HasValue && dto.SchoolCityId.Value != 0) { query = query.Where(y => y.SchoolCityId == dto.SchoolCityId.Value); } // 更多属性按相同逻辑添加 // 执行查询 var result = query.ToList();
方案2:反射+表达式树(适合属性较多场景)
如果DTO中有大量可筛选属性,手动写多个if会产生重复代码,可通过反射+表达式树动态生成筛选条件:
var query = data.AsQueryable(); // 获取DTO中所有可空Int类型且有值(非0)的属性 var validFilterProps = typeof(FilterParamsDto).GetProperties() .Where(p => p.PropertyType == typeof(int?)) .Select(p => new { PropName = p.Name, PropValue = (int?)p.GetValue(dto) }) .Where(x => x.PropValue.HasValue && x.PropValue.Value != 0); foreach (var prop in validFilterProps) { // 动态构建筛选表达式 var param = Expression.Parameter(typeof(YourDataModel), "y"); // 替换为实际数据实体类型 var entityProp = Expression.Property(param, prop.PropName); var constantVal = Expression.Constant(prop.PropValue.Value); var equalExpr = Expression.Equal(entityProp, constantVal); var filterLambda = Expression.Lambda<Func<YourDataModel, bool>>(equalExpr, param); query = query.Where(filterLambda); } var result = query.ToList();
注意事项
- 原查询中存在笔误:
y.IntakeId == dto.CountryId应为y.CountryId == dto.CountryId,需修正避免逻辑错误 - 若使用EF Core等ORM,链式Where不会导致多次数据库查询,最终会合并为一个带多个AND条件的SQL语句
内容的提问来源于stack exchange,提问作者Ashwani Bhardwaj
相关产品推荐
相关产品推荐

