在相同的IEnumerable上循环LINQ查询 - 我缺少什么?

时间:2011-01-21 11:07:27

标签: c# linq

使用字典我循环遍历IEnumerable并每次根据过滤字典应用过滤器。 FilterType枚举决定过滤器应该应用于哪个字段,字符串是自由文本过滤器本身。

我遇到的问题是,如果我向字典添加多个过滤器(即FilterType.Customer和FilterType.Name过滤器),过滤器将应用于Customer和Name字段,但freetext过滤器将取自添加的第一个过滤器。即在下面的代码中,nameFilterBox.Text应用于LINQ查询中的Name和Customer字段,而customerFilterBox.Text根本没有应用。因此,字典的FilterType部分被读取两次,但字符串过滤器仅从第一个读取。

我真的不明白为什么会这样。

static IEnumerable<Jobb> jobQuery = GetInitialJobQuery();

Dictionary<FilterType, string> filters = new Dictionary<FilterType, string>();
filters.Add(FilterType.Name, nameFilterBox.Text);
filters.Add(FilterType.Customer, customerFilterBox.Text);

foreach (var filter in filters)
{
    switch (filter.Key)
    {
        case FilterType.Customer:
            jobQuery = jobQuery.Where(x => x.KUNDREF != null &&
                       x.KUNDREF.ToLower().Contains(filter.Value.ToLower()));
            break;

        case FilterType.Name:
            jobQuery = jobQuery.Where(x => x.JOBBESKR != null &&
                       x.JOBBESKR.ToLower().Contains(filter.Value.ToLower()));
            break;
    }
}

2 个答案:

答案 0 :(得分:3)

这是foreach循环变量捕获错误。你的lambda在filters的最后一个值上运行,而不是你想的那个。

解决方法是在foreach循环中声明一个新变量:

foreach (var filterForeach in filters)
{
    var filter = filterForeach;  // for the lambdas
    switch (filter.Key)
    {
        case FilterType.Customer:
            jobQuery = jobQuery.Where(x => x.KUNDREF != null &&
                       x.KUNDREF.ToLower().Contains(filter.Value.ToLower()));
            break;

        case FilterType.Name:
            jobQuery = jobQuery.Where(x => x.JOBBESKR != null &&
                       x.JOBBESKR.ToLower().Contains(filter.Value.ToLower()));
            break;
    }
}

或者,由于你只在lambdas中使用filter.Value,你也可以这样做:

foreach (var filter in filters)
{
    var value = filter.Value.ToLower();  // for the lambdas
    switch (filter.Key)
    {
        case FilterType.Customer:
            jobQuery = jobQuery.Where(x => x.KUNDREF != null &&
                       x.KUNDREF.ToLower().Contains(value));
            break;

        case FilterType.Name:
            jobQuery = jobQuery.Where(x => x.JOBBESKR != null &&
                       x.JOBBESKR.ToLower().Contains(value));
            break;
    }
}

Eric Lippert在此发表了关于此事的博文:Closing over the loop variable considered harmful

答案 1 :(得分:1)

我不确定,但我认为它与从枚举器中读取值的方式有关,因此它不会在lambda的闭包中结束。由于值在循环之后使用,您将使用枚举器的Current属性中剩余的值,即最后一项。

尝试将值复制到局部变量中,使其最终出现在闭包中:

string value = filter.Value.ToLower();
switch (filter.Key) {
  case FilterType.Customer:
    jobQuery = jobQuery
      .Where(x => x.KUNDREF != null && x.KUNDREF.ToLower().Contains(value));
    break;
case FilterType.Name: {
  jobQuery = jobQuery
    .Where(x => x.JOBBESKR != null && x.JOBBESKR.ToLower().Contains(value));
  break;
}