使用 LINQ 缩小结果集的范围

本文关键字:范围 结果 LINQ 缩小 使用 | 更新日期: 2023-09-27 17:56:31

假设我有一个名为GetCatsByColor的方法,它将颜色作为字符串,一个将名称作为字符串的方法GetCatsByName,以及GetCatsByBirthDate,该方法需要两个DateTime作为时间范围。

现在假设我有一个CatFilter类,其中包含List名称、List颜色和两个DateTime,表示时间跨度的"开始"日期和"结束"日期。我正在尝试做的是创建一个GetFilteredCats方法,该方法采用这些Filter对象之一并返回符合给定Filter规范的 Cat 集合。

我很难想出一种获得所需结果的有效方法,理想情况下使用 LINQ/lambda 表达式。

进行这种加入的最佳方法是什么?我应该查看哪些扩展方法?在foreach循环中修改集合通常是不可取/不可能的,那么我的策略应该是什么?

使用 LINQ 缩小结果集的范围

我通常做的是在 where 子句中进行检查,该子句在执行实际过滤器之前检查是否需要过滤器。当运行时需要评估筛选器时,如果不需要筛选器,则会完全跳过它。

public class CatFilter
{
    public List<string> Names = new List<string>();
    public List<string> Colors = new List<string>();
    public DateTime? BirthDateStartRange = null;
    public DateTime? BirthDateEndRange = null;
}
public List<Cat> GetFilteredCats(CatFilter filter)
{
    List<Cat> result = new List<Cat>();
    var query = cats
        .Where(a => !filter.Names.Any() || filter.Names.Contains(a.Name))
        .Where(a => !filter.Colors.Any() || filter.Colors.Contains(a.Color))
        .Where(a => filter.BirthDateStartRange == null || a.DateOfBirth >= filter.BirthDateStartRange)
        .Where(a => filter.BirthDateEndRange == null || a.DateOfBirth <= filter.BirthDateEndRange);
    result.AddRange(query);
    return result;
}

然后这样称呼它

cats.Add(new Cat("Felix", "Black", DateTime.Today.AddDays(-1)));
cats.Add(new Cat("Garfield", "Orange", DateTime.Today.AddDays(-10)));
CatFilter filter = new CatFilter();
filter.Names.Add("Garfield");
List<Cat> result = GetFilteredCats(filter);

正确的方法是使方法GetFilteredCats,接受您的过滤器并通过 LINQ 组合返回正确的猫:

IEnumerable<Cat> cats = //.. get all cats here
if (filter.FilterByColor)
   cats = cats.Where(c=>c.Color = filter.Color);
if (filter.FilterByName)
   cats = cats.Where(c=>c.Name = filter.Name);
if (filter.FilterByDate)
   cats = cats.Where(c=>c.Date > filter.FromDate && c.Date < filter.ToDate)
return cats.ToList(); // finally filter data and return them.

在性能的情况下。我认为这在逻辑上不能使用不同的 aproach 来完成。但是当你开始打成群的猫时,这将成为一个问题。此时,应改用数据库。为了方便起见,它们具有巧妙的索引和聚类。

这样的东西应该可以工作,请注意它没有经过测试

List<string> names = new List<string>();
            List<Color> colors = new List<Color>();
            List<DateTime> dobs = new List<DateTime>();
            List<cat> cats = new List<cat>();

            var filtered = from c in cats
                           join n in names on c.name equals n
                           join cl in colors on c.color equals cl
                           join db in dobs on c.dob equals db
                           select c;

你也可以有一些包含两个日期的列表,在这种情况下,你需要输入一个where条件,其中c.dob <= date1 && c.dob>= date2,或类似的东西。希望这有帮助。

您可以使用表达式树。当 CatFilter 对象传递给 GetFilteredCats 方法时,根据在此对象上设置的属性,生成表达式(即 在下面的伪代码中看到),您可以连接并用于构建完整的 LINQ 查询。

像这样:

Expression catFilter =
from cat in Cats
    where <Expression> and <Expression> and ...
select cat

然后简单地编译(Expression.Compile)并执行。