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

本文关键字:过滤器 构建 搜索 复杂 Linq 基于 | 更新日期: 2023-09-27 18:02:36

想知道是否有一个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();
                }
            }
   }

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

一种(残酷的)方法:

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"}

您可以编写一个方法来检查给定的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,但对于您的用例来说,这似乎是多余的。