我的应用程序中有搜索功能,它有4个标准,基本上是位置,状态, PropertyType [字符串列表] 和 PriceRange [最低和最高价格] 及以下是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中选择值是可选的,它可以是单个或多个。所以我需要过滤数据库,如果用户没有选择任何项目,那么这个List
将是null
,因为它是一个可选的搜索条件。
例如
状态值可以是&#34;正在进行&#34;,&#34;即将到来&#34;和&#34;已完成&#34;。所以我在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
,但是解决这个问题的解决方案并不是很有用吗?基本上我如何在这些情况下合并搜索查询?
答案 0 :(得分:2)
发布的答案是正确的,并为您提供了所需的解决方案,如果您需要在几个地方使用此行为,我将继续检查null。如果这个请求在很多地方都是重复的,我将采用以下解决方案。
如果您正在进行大量此类检查,还有另一种更清洁的方法,就是添加Extension Methods来为您执行此操作。
扩展方法使您可以向现有类型“添加”方法 无需创建新的派生类型,重新编译或其他方式 修改原始类型。扩展方法是一种特殊的方法 静态方法,但它们被称为就像它们是实例方法一样 扩展类型。对于用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();
答案 1 :(得分:2)
另一种选择是在查询之外进行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的内容。
答案 2 :(得分:1)
因此,如果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。
答案 3 :(得分:1)
您可以在where子句中添加空检查:
var query=db.tblProperties.Where(p => smodel.StatusID == null || smodel.StatusID.Contains(p.PropertyLocation))
.Select(x=>x).ToList();
答案 4 :(得分:1)
C#使用布尔表达式的Short-circuit evaluation。这意味着一旦确定了结果,C#就会停止计算表达式。
例如,在a && b
中,如果false
为a
,则结果为false
,因此不会评估b
。在a || b
中,如果true
为a
,则结果为true
,因此不会评估b
。
您可以通过添加null-test来使用它来保护您免受异常的影响:
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();