LINQ:将此嵌套到单个查询中

时间:2012-09-20 15:45:47

标签: c# linq linq-to-entities

我知道在SO和其他地方有一些brazillion LINQ嵌套查询示例,但它对我来说没有意义。如果有人可以解释这个就像我五岁那样,我会非常感激。这都是伪模糊的,所以请耐心等待这个人为的例子。

我有一个具有以下功能的EF模型:

public class Car(){
  public String Vin {get; private set;}
  public string Type {get; private set;}
  public List<Option> Options {get; set;}
}

public class Option(){
  public string Name { get; set; }
}

我从存储库中获取了IQueryable<Car> cars的集合。

足够简单。目标是:

  1. 列出所有车型(比如“卡车”,“suv”,“小型货车”等)
  2. 在每种类型下,都有一个选项名称的子列表,该列表存在于该类型的汽车上。
  3. 在每个选项下,列出具有该选项且属于该类型的每辆车的VIN。
  4. 所以列表看起来像:

    • 卡车
      • 预告片
        • VIN1
        • VIN8
      • 卡车坚果
        • VIN2
        • VIN3
        • vin4
      • 枪架
        • VIN1
      • 折叠后座
        • VIN2
        • VIN3
    • 面包车
      • 旋转座椅
        • VIN6
      • DVD播放器
        • VIN6
        • vin10
      • 折叠后座
        • vin6
        • vin10
    • 越野车
      • 折叠后座
        • vin9
        • VIN5

    你可能会明白这个想法。我知道我可以按类型对汽车进行分组,如下所示:

    from c in cars
    group by c.Type into g
    

    但我认为我需要做的是将Vin分组到Option并将结果分组到Type中。我也认为我可能需要

    cars.SelectMany(c => c.Options)
        .Select(o => o.Name)
        .Distinct();
    

    获取唯一选项名称的列表,但我不确定a)这是最有效的方法,以及b)如何将其合并到我的分组查询中。我真的不明白如何编写嵌套分组查询来实现这一点 - 第一组是外部组还是内部组?

    我对此的理解低于补救,所以请再次:解释,就像我五岁。

    感谢所有人。

3 个答案:

答案 0 :(得分:2)

这肯定不是一个微不足道的问题。

我会这样做:

cars.SelectMany(c => c.Options.Select(o => new { Car = c, Option = o.Name }))
    .GroupBy(x => x.Car.Type)
    .Select(x => new
                 {
                     Type = x.Key,
                     Options = x.GroupBy(y => y.Option, y => y.Car.Vin)
                                .Select(y => new { Option = y.Key,
                                                   Cars = y.ToList() } )
                 });

此查询执行以下操作:

  1. 我们更改了我们工作的数据,以便更容易处理:您希望在汽车上方拥有选项。这意味着最终结果会让每辆车都有多种选择,所以我们真正需要的是一个(Option,Car)元组列表。

    我们用

    实现这一目标
    cars.SelectMany(c => c.Options.Select(o => new { Car = c, Option = o.Name }))
    

    这基本上说:对于每辆车和每个选项,选择一个新的匿名类型与汽车和选项名称作为属性。我们将其命名为匿名类型Ano1。

    结果将是IEnumerable<Ano1>

  2. 此平面数据现在按车型分组。这意味着对于每种车型,我们都有一个Ano1实例列表。因此,我们现在有一个组列表,每个组都有一个Ano1实例列表。

  3. 在这个组列表中,我们发出一个选择。对于每个组(=汽车类型),选择返回一个新的匿名类型,其中包含汽车类型的属性 - 这样信息不会丢失 - 以及选项的属性。

  4. 上一步中的选项最终将是具有Option和Cars属性的匿名类型列表。为了获得这些数据,我们通过Option对我们的所有Ano1实例进行分组,并选择汽车的VIN作为组内的元素。这些组现在转换为新的匿名类型,其中包含选项名称的属性和汽车的属性。

  5. 查询并不简单,解释也是如此。如果不清楚,请询问。

答案 1 :(得分:2)

这个linq-to-entities查询可以解决问题

var query = from c in cars
            group c by c.Type into g
            select new {
              Type = g.Key,
              Options = from o in g.Options.SelectMany(x => x.Options).Distinct()
                        select new {
                          o.Name,
                          Vins = from c in cars
                                 where c.Options.Any(x => x.Name == o.Name)
                                 where c.Type == g.Key
                                 select c.Vin
                        }
            }

答案 2 :(得分:2)

这不会很漂亮,我不确定L2E会处理这个问题,您可能需要从数据库中选择整个列表并在L2O中执行此操作:

var result = cars.GroupBy(c => c.Type)
            .Select(c => new {
               Type = c.Key,
               Options = c.SelectMany(x => x.Options)
                           .GroupBy(x => x.Name)
                           .Select(x => new {
                                  Option = x.Key ,
                                  Vins = c.Where(y => y.Options.Any(z => z.Name == x.Key)).Select(z => z.Vin)
                           })
            });

实例(仅限卡车建模,但适用于所有人):http://rextester.com/OGD12123