使用LINQ缩小结果集

时间:2011-05-12 21:29:56

标签: c# linq collections join lambda

假设我有一个名为GetCatsByColor的方法,它将颜色作为字符串,方法GetCatsByName将名称作为字符串,GetCatsByBirthDate采用两个DateTime这是一段时间。

现在说我有一个CatFilter类,其中包含List个名称,List个颜色和两个DateTime s,代表“来自”日期和“到”时间的日期。我要做的是创建一个GetFilteredCats方法,该方法接受其中一个Filter个对象,并返回符合给定Filter规范的Cats集合。

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

进行此类加入的最佳方式是什么?我应该考虑哪些扩展方法?通常不建议/可能在foreach循环中修改集合,那么我的策略应该是什么?

4 个答案:

答案 0 :(得分:1)

我通常做的是检查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);

答案 1 :(得分:0)

正确的方法是制作方法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.

如果有表现。我不认为这可以用不同的方法来完成。但是当你开始击中数百万只猫时,这将成为问题。此时,应该使用数据库。为方便起见,它们具有聪明的索引和聚类。

答案 2 :(得分:0)

这样的东西应该有用,请注意它没有经过测试

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&lt; = date1&amp;&amp; c.dob&gt; = date2,或类似的东西。 希望这会有所帮助。

答案 3 :(得分:0)

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

类似的东西:

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

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