使用" contains"时检查List是否为空。在LINQ查询中,否则选择所有记录

时间:2016-01-15 17:51:19

标签: c# asp.net-mvc linq

我的应用程序中有搜索功能,它有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中选择的值列表将存储在CategoriesIdLocationIDStatusID中。现在从每个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.StatusIDnull,即用户未选择任何值,则此查询将失败并显示exception。那么如何克服这个呢?还有如何在没有选择值时获取所有records?通过 this post ,但是解决这个问题的解决方案并不是很有用吗?基本上我如何在这些情况下合并搜索查询?

5 个答案:

答案 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中,如果falsea,则结果为false,因此不会评估b。在a || b中,如果truea,则结果为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();