构建基于Linq的复杂搜索过滤器

时间:2016-08-16 21:31:12

标签: c# asp.net-mvc linq

想知道是否有针对这种复杂情况的linq解决方案;例如,客户选择颜色:红色,尺寸:大,类型:棉花。如果我确定客户每次都会选择这些过滤器,我会做这样的事情:

var shirts = ListOfShirts.Where(i=> i.Color.Contains("red") && Size.Contains("Large") && Type.Contains("Cotton"));

以下是当前结构和一个从视图中接受大量过滤器的操作方法:

public partial class Shirt {
   public long ID { get; set; }
   public string Color  { get; set; }
   public string Size { get; set; }
   public string Type { get; set; }
}

public class SelectedFilter {
   public string Name { get; set; } // Filter Menu Title
   public string Value { get; set; }
}

控制器

public ActionResult Filter(IList<SelectedFilter> selectedFilters) {
       var model = new List<Shirt>();
       Shirt ListOfShirts = db.GetAllShirts();
       var groups = selectedFilters.GroupBy(i => i.Name);

            foreach (var g in groups)
            {
                var GroupName = g.FirstOrDefault().name;
                var GroupValues = g.Select(i => i.value).ToList();

                if (g.FirstOrDefault().name == "Color")
                {
                    model = ListOfShirts.Where(i => GroupValues.Contains(i.Color)).ToList();
                }
                else if (g.FirstOrDefault().name == "Size")
                {
                    model = ListOfShirts.Where(i => GroupValues.Contains(i.Size)).ToList();
                }
                else if (g.FirstOrDefault().name == "Type")
                {
                    model = ListOfShirts.Where(i => GroupValues.Contains(i.Type)).ToList();
                }
            }
   }

2 个答案:

答案 0 :(得分:1)

您可以编写一个方法来检查给定的Shirt是否在过滤器中,然后使用All检查每个过滤器。

public static void Filter(IList<SelectedFilter> selectedFilters)
{
    var filters = selectedFilters
        .GroupBy(i => i.Name);

    var filteredShirts = db
        .GetAllShirts()
        .Where(shirt => filters.All(filter => ShirtInFilter(filter, shirt)));
}

public static bool ShirtInFilter(
    IGrouping<string, SelectedFilter> filter,
    Shirt shirt)
{
    var values = filter.Select(i => i.Value);

    switch (filter.Key)
    {
        case "Color":
            return values.Contains(shirt.Color);
        case "Size":
            return values.Contains(shirt.Size);
        case "Type":
            return values.Contains(shirt.Type);
        default:
            return false;
    }
}

如果您想使用反射访问属性,switch可以被删除,但这对您的用例来说似乎有些过分。

答案 1 :(得分:1)

一种(野蛮)方式:

public ActionResult Filter(IList<SelectedFilter> selectedFilters) {
       var model = new List<Shirt>();
       IEnumerable<Shirt> ListOfShirts = db.GetAllShirts();

        IEnumerable<Shirt> filteredListOfShirts = ListOfShirts
           .Where(shirt => {
               return (
                    (selectedFilters.Any(filter => filter.Name == "Color" && !string.IsNullOrWhiteSpace(filter.Value)) ? // is Color filter set?
                        selectedFilters.Any(filter =>
                        {
                            return (filter.Name == "Color" && shirt.Color == filter.Value);
                            }) : true)
                    &&
                    (selectedFilters.Any(filter => filter.Name == "Size" && !string.IsNullOrWhiteSpace(filter.Value)) ? // is Size filter set?
                        selectedFilters.Any(filter =>
                       {
                           return (filter.Name == "Size" && shirt.Size == filter.Value);
                       }) : true)
                    &&
                    (selectedFilters.Any(filter => filter.Name == "Type" && !string.IsNullOrWhiteSpace(filter.Value)) ? // is Type filter set?
                        selectedFilters.Any(filter =>
                        {
                            return (filter.Name == "Type" && shirt.Type == filter.Value);
                        }) : true)
                    );
               });

        model = filteredListOfShirts.ToList();

   }

如果您有多个具有相同名称的过滤器值

,这也应该有效
{Name="Color",Value="Red"},{Name="Color",Value="Blue"}