在 LINQ 查询中使用“包含”时检查列表是否不为 null,否则选择所有记录
本文关键字:null 记录 选择 是否 列表 查询 LINQ 检查 包含 | 更新日期: 2023-09-27 18:30:43
我的应用程序中有一个搜索功能,它有 4 个条件,基本上是位置、状态、属性类型 [字符串列表] 和价格范围[最小和最高价格],下面是model
public class SearchFilters
{
public SearchFilters()
{
MinPrice = "10000";
MaxPrice = "8000000";
}
public IEnumerable<SelectListItem> Categories { get; set; }
public string[] CategoriesId { get; set; }
public IEnumerable<SelectListItem> Locations { get; set; }
public string[] LocationID { get; set; }
public IEnumerable<SelectListItem> Status { get; set; }
public string[] StatusID { get; set; }
public string MinPrice { get; set; }
public string MaxPrice { get; set; }
}
当在controller
从SelectList
中选择的值列表中接收到数据时,将存储在CategoriesId
、LocationID
和StatusID
中。现在,从每个列表中选择值是可选的,它可以是单个或多个。所以我需要过滤数据库,如果用户没有选择任何项目,那么这个List
将被null
,因为它是一个可选的搜索条件。
例如
状态值可以是"正在进行"、"即将推出"和"已完成"。所以我在下面使用下面的LINQ
来提取数据。
[HttpGet]
public ActionResult Search(SearchFilters smodel)
{
var query=db.tblProperties.Where(p => smodel.StatusID.Contains(p.PropertyLocation)).Select(x=>x).ToList();
//.....
//.....
}
刚刚添加了一个属性比较来演示
这将毫无问题地返回记录,但如果出现此smodel.StatusID
null
即用户未选择任何值,则此查询将失败并显示exception
。那么如何克服这个问题呢?还有,如何在没有选择任何值时获取所有records
?经历了this post
但那里的解决方案对克服这个问题没有用?基本上,在这些情况下如何合并搜索查询?
发布的答案是正确的,并为您提供了所需的解决方案,如果您在几个地方需要此行为,我将检查 null。如果此请求在许多地方重复,我将采用以下解决方案。
如果您正在执行大量此类检查,还有另一种更干净的方法是添加扩展方法来为您完成。
扩展方法使您能够将方法"添加"到现有类型 无需创建新的派生类型、重新编译或其他方式 修改原始类型。扩展方法是一种特殊的 静态方法,但它们被调用,就好像它们是实例方法一样 扩展类型。对于用 C# 和 Visual Basic 编写的客户端代码, 调用扩展方法之间没有明显区别 以及在类型中实际定义的方法。
法典:
public static class CollectionExtension
{
public static bool CheckContainsIfHasValue<T>(this IEnumerable<T> source, T value)
{
return source == null || source.Contains(value);
}
}
用法:
var query = db.tblProperties
.Where(p => smodel.StatusID.CheckContainsIfHasValue(p.PropertyLocation))
.ToList();
那么如果smodel.StatusID
为空,你想返回所有记录吗?
var query=db.tblProperties.Where(p => smodel.StatusID == null || smodel.StatusID.Contains(p.PropertyLocation))
.Select(x=>x).ToList();
因此,如果你现在看Where
条款,如果smodel.StatusID == null
,那么每个项目都将通过Where
条款。请注意,由于快捷方式,不会调用.Contains
(如果OR的第一个项为真,则评估第二个项没有意义,因此不会)。
您也可以考虑执行以下操作:
.Where(p => smodel.StatusID == null ||
!smodel.StatusID.Any() ||
smodel.StatusID.Contains(p.PropertyLocation))
这样,您既要检查StatusID
是否为空,又检查集合StatusID
是否为空。
如果可以将StatusID
设置为默认为空集合而不是 null(例如,在 smodel
的任何类的构造函数中设置它),则可以执行以下操作:
.Where(p => !smodel.StatusID.Any() ||
smodel.StatusID.Contains(p.PropertyLocation))
由于您不再需要null
检查,并且Any
应该可以转换为 LINQ to SQL。
另一种选择是在查询之外执行null
检查
var initialQuery = db.tblProperties;
if(smodel.StatusID != null)
{
initialQuery = initialQuery.Where(p => smodel.StatusID.Contains(p.PropertyLocation));
}
var query = initialQuery.ToList();
或作为辅助方法
public static IEnumerable<T> ConditionalWhere<T>(
this IEnumerable<T> collection,
Func<bool> condition,
Expression<Func<T, bool>> predicate)
{
if(condition())
return collection.Where(predicate);
return collection;
}
然后
var query = db.tblProperties.ConditionalWhere(
() => smodel.StatusID != null,
p => smodel.StatusID.Contains(p.PropertyLocation));
你可以把它们链接在一起
var query = db.tblProperties.ConditionalWhere(
() => smodel.StatusID != null,
p => smodel.StatusID.Contains(p.PropertyLocation))
.ConditionalWhere(
() => someOtherCollection != null,
p => someOtherCollection.Contains(p.PropertyLocation));
这将避免多次运行 Linq-to-Objects 的condition
,并允许您使用无法转换为 SQL for EF 或 Linq-to-SQL 的内容。
您可以简单地在 where 子句中添加一个空检查:
var query=db.tblProperties.Where(p => smodel.StatusID == null || smodel.StatusID.Contains(p.PropertyLocation))
.Select(x=>x).ToList();
C# 使用布尔表达式的短路计算。这意味着 C# 在确定表达式结果后立即停止计算表达式。
例如在a && b
中,如果a
false
,则已知结果是false
的,因此b
不会被评估。在a || b
已知结果是true
如果a
是true
,所以b
不会被评估。
您可以使用它通过添加 null 测试来保护您免受异常的影响:
var query = db.tblProperties
.Where(p => smodel.StatusID == null ||
smodel.StatusID.Contains(p.PropertyLocation))
.ToList();
您也可以删除.Select(x=>x)
部分,因为它什么都不做。
如果使用 LINQ to EF,则上述文本不适用。不能对集合执行空检查,因为这不能转换为 SQL。而是在以下之前进行检查:
bool ignore = smodel.StatusID == null || !smodel.StatusID.Any();
var query = db.tblProperties
.Where(p => ignore ||
smodel.StatusID.Contains(p.PropertyLocation))
.ToList();