【问题标题】:LINQ - How to ignore empty lists in whereLINQ - 如何在哪里忽略空列表
【发布时间】:2020-01-08 08:41:11
【问题描述】:

这是我使用 LINQ 的第一步。我有两个过滤器列表作为参数,也可以为空。如果我以这种方式执行代码,我不会从空列表中获取任何值。
如果要忽略空列表,代码应该是什么样的?

    public List<PersonDTO> GetPersons(int pageNumber, int pageSize, List<string> departments, List<string> locations, string filterText)
    {
        if (filterText == null)
        {
            filterText = "";
        }

        List<Person> personsList = _dbContext.Persons
            .Where(a => (a.firstName.ToLower().Contains(filterText.ToLower()) || a.lastName.ToLower().Contains(filterText.ToLower()))
                        && departments.Contains(a.department) 
                        && locations.Contains(a.location))
            .Skip(pageNumber * pageSize).Take(pageSize).ToList();

        return _mapper.Map<List<PersonDTO>>(personsList);
    }

【问题讨论】:

  • 删除&amp;&amp; departments.Contains(a.department)。将Where 的返回值分配给一个临时变量。检查departmentsCount。如果它不是零 - 使用tempVariable = tempVariable.Where(z =&gt; departments.Contains(z.department)。起泡、冲洗、重复(locations)。然后稍后在 temp 变量上调用 Skip
  • 你能用 Linq 发布一个minimal reproducible example 来反对,或者甚至用好的旧循环吗?
  • 可以添加更多测试用例吗?

标签: c# .net linq .net-core


【解决方案1】:

处理它们为空的情况不是在查询中而是在if

IEnumerable<Person> persons = _dbContext.Persons;
if(!string.IsNullOrEmpty(filterText))
{
    string lowerFilterText = filterText.ToLower();
    persons = persons
       .Where(p => p.firstName.ToLower().Contains(lowerFilterText) || a.lastName.ToLower().Contains(lowerFilterText));
}
if(departments.Any())
{
    persons = persons.Where(p => departments.Contains(p.department));
}
if(locations.Any())
{
    persons = persons.Where(p => locations.Contains(p.location));
}
List<Person> personList = persons.Skip(pageNumber * pageSize).Take(pageSize).ToList();

由于 LINQ 的延迟执行,这将仅在最终的ToList 处执行最终查询一次。

【讨论】:

  • 如果你添加它,完美。好像是踩到上面来初始化的
【解决方案2】:

看看是否否定 Any() 可以帮助你。 示例:

string[] arr = new string[10] {"a","b","c","d","e","f","g","h","i","j"};
List<string> vowels = new List<string>() {"a","e","i","o","u"};
List<string> empty = new List<string>();

arr.Where(letter => vowels.Contains(letter)); 
//yields "a","e","i"
arr.Where(letter => (!empty.Any() || empty.Contains(letter))); 
//yields "a","b","c","d","e","f","g","h","i","j"

按照您的示例,我将链接 .Where() 表达式,而不是将其全部放入一个大的单个表达式中。

【讨论】:

    【解决方案3】:

    您需要将列表括在括号中并使用计数验证使其对每个列表都是可选的,如下所示:

    List<Person> personsList = _dbContext.Persons
        .Where(a => 
            (a.firstName.ToLower().Contains(filterText.ToLower()) || 
            a.lastName.ToLower().Contains(filterText.ToLower())) && 
            (departments.Count == 0 || departments.Contains(a.department)) && 
            (locations.Count == 0 || locations.Contains(a.location)))
        .Skip(pageNumber * pageSize)
        .Take(pageSize)
        .ToList();
    

    通过这种方式,您可以将数组条件切换为可选状态,因此只要数组中有一个项目 !(list.Count == 0),它就会尝试评估过滤器。

    【讨论】:

      【解决方案4】:

      你需要这样的东西:

      Why would you use Expression> rather than Func?

      Expression<Func<Persons, bool>> expresionFinal = c => c.Active;
      
          if (departments.Any())
                      {
                          Expression<Func<Persons, bool>> expresionDepartments = c => departments.Contains(p.department);
                          expresionFinal = PredicateBuilder.And(expresionFinal, expresionDepartments);
                      }
      
      IQueryable query = dataContext.Persons;
       query = query.Where(expresionFinal);
      

      【讨论】:

        【解决方案5】:

        尝试使用.Where(s =&gt; !string.IsNullOrWhiteSpace(s)) 过滤掉列表中的空字符串条目。

        【讨论】:

          【解决方案6】:

          有几种方法可以检查列表是否为空: 1. 如果(list.Count() >0) 2. 最好的方法是使用“Any”而不是“Where”,这将返回布尔结果,如果为真则有一些数据,否则什么都没有。

          【讨论】:

            【解决方案7】:

            如果您想在 where 子句中忽略空列表(部门和位置),您应该可以使用 Any():

            public List<PersonDTO> GetPersons(int pageNumber, int pageSize, List<string> departments, List<string> locations, string filterText)
            {
                if (filterText == null)
                {
                    filterText = "";
                }
            
                List<Person> personsList = _dbContext.Persons
                    .Where(a => (a.firstName.Contains(filterText, StringComparison.OrdinalIgnoreCase)
                                  || a.lastName.Contains(filterText, StringComparison.OrdinalIgnoreCase))
                                && (!departments.Any() || departments.Contains(a.department))
                                && (!locations.Any() || locations.Contains(a.location)))
                    .Skip(pageNumber * pageSize).Take(pageSize).ToList();
            
                return _mapper.Map<List<PersonDTO>>(personsList);
            }
            

            以及包含的代码:

            public static class StringExtensions
            {
                public static bool Contains(this string source, string toCheck, StringComparison comp)
                {
                    return source?.IndexOf(toCheck, comp) >= 0;
                }
            }
            

            来源:https://stackoverflow.com/a/444818/1248177

            【讨论】:

              【解决方案8】:

              这应该可行。如果可以的话,你应该在调用 ToList() 之前真正建立你的查询。这将使您能够分阶段做事并使您的应用程序更有效率。

              public List<PersonDTO> GetPersons(int pageNumber, int pageSize, List<string> departments, List<string> locations, string filterText = "")
              {
                  List<Person> personList = new List<Person>();
              
                  if (!string.IsNullOrEmpty(filterText)) {
                      personsList = _dbContext.Persons
                          .Where(a => (a.firstName.ToLower().Contains(filterText.ToLower()) || a.lastName.ToLower().Contains(filterText.ToLower()))
                                  && departments.Contains(a.department) 
                                  && locations.Contains(a.location)).ToList();
                  } else {
                      personList = _dbContext.Persons.ToList();
                  }
              
                      personList = personList.Skip(pageNumber * pageSize).Take(pageSize).ToList();
              
                  return _mapper.Map<List<PersonDTO>>(personsList);
              }
              

              这是另一个使用 IQueryable 的示例。

              public List<PersonDTO> GetPersons(int pageNumber, int pageSize, List<string> departments, List<string> locations, string filterText = "")
                  {
                      IQueryable<List<Person>> personQuery = _dbContext.Persons.AsQueryable();
              
                      if (!string.IsNullOrEmpty(filterText))
                      {
                          personQuery = personQuery
                              .Where(a => (a.firstName.ToLower().Contains(filterText.ToLower()) || a.lastName.ToLower().Contains(filterText.ToLower()))
                                      && departments.Contains(a.department)
                                      && locations.Contains(a.location));
                      }
              
                      personQuery = personQuery.Skip(pageNumber * pageSize).Take(pageSize);
              
                      return _mapper.Map<List<PersonDTO>>(personQuery.ToList());
                  }
              

              这是我如何做你想做的事的一个例子。

              public List<CourseSearchDetail> GetPaginated(SearchRequest searchRequest, bool admin, out int totalRecords,
                      out int recordsFiltered)
                  {
                      var query = _courseRepo
                          .GetDataTableQuery();
              
                      if (!admin) query = query.Where(x => x.CourseDate > DateTime.Now);
              
                      var courseList = query.ToList();
              
                      totalRecords = courseList.Count();
              
                      if (!string.IsNullOrEmpty(searchRequest.Search.Value))
                          courseList = courseList.Where(x => x.CourseTitle.ToLower().Contains(searchRequest.Search.Value.ToLower())).ToList();
              
                      recordsFiltered = courseList.Count();
              
                      if (searchRequest.Order == null)
                          courseList = courseList.OrderByDescending(x => x.CourseDate).ToList();
                      else
                          courseList = courseList.OrderResults(searchRequest);
                      var skip = searchRequest.Start;
                      var pageSize = searchRequest.Length;
              
                      courseList = pageSize > 0
                          ? courseList.Skip(skip).Take(pageSize).ToList()
                          : courseList.ToList();
              
                      return courseList;
                  }
              

              【讨论】:

              • 如果需要,我可以为 SearchRequest 类添加 sn-ps。
              猜你喜欢
              • 2014-01-03
              • 2011-09-03
              • 1970-01-01
              • 1970-01-01
              • 2012-07-25
              • 1970-01-01
              • 1970-01-01
              • 2013-08-31
              • 2012-05-16
              相关资源
              最近更新 更多