【问题标题】:C# - How to do multiple contains filters on an object listC# - 如何在对象列表上执行多个包含过滤器
【发布时间】:2017-09-18 15:02:33
【问题描述】:

我有一个数据表,我正在尝试对列实施服务器端过滤。我允许每列查询多个字符串,并试图确定单步执行所有过滤器的最佳方式。下面是一个表格对象的例子,为了简单起见,只有两个参数。

//table models from db
public class Parts
{
    public string PartName { get; set; }
    public virtual Series Series { get; set; }
}

public class Series
{
    public string SeriesName { get; set; }
}

//This is what is passed in from the datatable filter query
public class PartsTable
{
    public string PartName { get; set; }

    public string SeriesName { get; set; }
}


public IEnumerable<Parts> PartsTableSearch(PartsTable table)
{
    //Queries come in as comma separated string
    var partNameList = table.PartName?.Split(',');
    var seriesNameList = table.SeriesName?.Split(',');

    //Gets and generates the list of Parts
    var fullList = GetParts();

    if (partNameList != null && partNameList.Length > 0)
    {        
        foreach (var partName in partNameList)
        {
            fullList = fullList.Where(p => p.PartName.ToLower().Contains(name.ToLower()));
        }
    }
    if (seriesNameList != null && seriesNameList.Length > 0)
    {
        foreach (var seriesName in seriesNameList)
        {
            fullList = fullList.Where(p => p.Series.SeriesName.ToLower().Contains(seriesName.ToLower()));
        }
    }

    return fullList;
}

这不适用于我想要的,因为对于每个参数(即 PartName),我需要能够返回所有具有 PartName 的对象,该 PartName 包含 partNameList 中的搜索字符串,然后从该结果中过滤进一步在包含 seriesNameList 中的搜索字符串的 SeriesNames 上,然后返回结果集。但是,partNameList 的查询可能是空白的并且只搜索 seriesName,反之亦然。有什么建议?我觉得这是一个明显的答案,我只是忽略了,尽管我搜索的任何其他内容仅适用于单个查询过滤器。提前致谢。

【问题讨论】:

  • 我建议你查看PredicateBuilder类,更多信息在this answer中。

标签: c# linq filter datatable contains


【解决方案1】:

这可以通过两种方式使用单个.Where 完成:

使用 .Contain() 将返回完全匹配:

public IEnumerable<Parts> PartsTableSearch(PartsTable table)
{
    //Queries come in as comma separated string
    var partNameList = table.PartName?.Split(',');
    var seriesNameList = table.SeriesName?.Split(',');

    //Gets and generates the list of Parts
    var fullList = GetParts()
        .Where(p => partNameList.Contains(p.PartName.ToLower())
            || seriesNameList.Contains(p.Series.SeriesName.ToLower()))
        .ToList();

    return fullList;
}

使用 .Any() 将返回部分匹配:

public IEnumerable<Parts> PartsTableSearch(PartsTable table)
{
    //Queries come in as comma separated string
    var partNameList = table.PartName?.Split(',');
    var seriesNameList = table.SeriesName?.Split(',');

    //Gets and generates the list of Parts
    var fullList = GetParts()
        .Where(p => partNameList.Any(n => n.Contains(p.PartName.ToLower())))
            || seriesNameList.Any(n => n.Contains(p.Series.SeriesName.ToLower())))
        .ToList();

    return fullList;
}

【讨论】:

  • 是的。 .Contains 将搜索确切的值。但是.Any 将返回部分匹配。修改了我的答案。
  • lists.Any() 在这个例子中是我需要的。 partNameList 是部分字符串的查询,p.PartName 是来自数据库的名称。
【解决方案2】:

为 LinqToSql 制作的。我被告知它不适用于实体框架。它应该与内存中的集合一起使用。

    public static Expression<Func<T, bool>> OrTheseFiltersTogether<T>(
        this IEnumerable<Expression<Func<T, bool>>> filters)
    {
        Expression<Func<T, bool>> firstFilter = filters.FirstOrDefault();
        if (firstFilter == null)
        {
            Expression<Func<T, bool>> alwaysTrue = x => true;
            return alwaysTrue;
        }

        var body = firstFilter.Body;
        var param = firstFilter.Parameters.ToArray();
        foreach (var nextFilter in filters.Skip(1))
        {
            var nextBody = Expression.Invoke(nextFilter, param);
            body = Expression.OrElse(body, nextBody);
        }
        Expression<Func<T, bool>> result = Expression.Lambda<Func<T, bool>>(body, param);
        return result;
    }

这样称呼:

public IEnumerable<Parts> PartsTableSearch(PartsTable table)
{
    var fullList = GetParts();
    var filters = new List<Expression<Func<Part, bool>>>();

    if (table.PartName != null)
    {
        var partNameList = table.PartName.ToLower().Split(',');
        foreach (var partName in partNameList)
        {
            filters.Add(p => p.PartName.ToLower().Contains(partName));
        }
    }
    if (table.SeriesName != null)
    {
        var seriesNameList = table.SeriesName.ToLower().Split(',');
        foreach (var seriesName in seriesNameList)
        {
            filters.Add(p => p.Series.SeriesName.ToLower().Contains(seriesName));
        }
    }

    Expression<Func<Part, bool>> filter = filters.OrTheseFiltersTogether();

    return fullList.Where(filter);
}

【讨论】:

    【解决方案3】:

    Yield return 用于您从不同查询中获得的每个结果集。不管它是否为空,所有结果都将收集在IEnumerable&lt;Parts&gt; 中。

    public IEnumerable<Parts> PartsTableSearch(PartsTable table)
    {
        //Queries come in as comma separated string
        var partNameList = table.PartName?.Split(',');
        var seriesNameList = table.SeriesName?.Split(',');
    
        //Gets and generates the list of Parts
        var fullList = GetParts();
    
        if (partNameList != null && partNameList.Length > 0)
        {        
            foreach (var partName in partNameList)
            {
                yield return fullList.Where(p => p.PartName.ToLower().Contains(name.ToLower()));
            }
        }
        if (seriesNameList != null && seriesNameList.Length > 0)
        {
            foreach (var seriesName in seriesNameList)
            {
                yield return fullList.Where(p => p.Series.SeriesName.ToLower().Contains(seriesName.ToLower()));
            }
        }
    
      yield return null;
    }
    

    【讨论】:

      【解决方案4】:

      这就是我最终让它工作的方式。

      var fullList = GetParts();        
      if (partNameList != null && partNameList.Length > 0)
              {
                  var tempList = new List<Parts>();
                  foreach (var partName in partNameList)
                  {
                      tempList.AddRange(fullList.Where(p => p.PartName.ToLower().Contains(partName.ToLower().Trim())));
                  }
                  fullList = tempList;
              }
              if (seriesNameList != null && seriesNameList.Length > 0)
              {
                  var tempList = new List<Parts>();
                  foreach (var seriesName in seriesNameList)
                  {
                      tempList.AddRange(fullList.Where(p => p.Series.SeriesName.ToLower().Contains(seriesName.ToLower().Trim())).ToList());
                  }
                  fullList = tempList;
              }
      return fullList.ToList();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-25
        • 2015-01-06
        • 2018-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多