Linq加入层次结构(一对多)

时间:2014-02-13 23:44:36

标签: c# linq join entities

我有3个实体,Computer,Monitor和PortNumber。实体监视器具有引用实体计算机的外键,而实体PortNumber具有引用实体Monitor的外键。以下规则适用

  • 计算机可以有多台显示器,但显示器只能属于 一台电脑。
  • 监视器可以有许多端口号但只有一个端口 号码只能属于一个监视器

我想编写一个Join来显示每台计算机的监视器和端口(监视器端口)的数量。我可以使用groupjoin加入两个实体,但无法弄清楚如何添加第三个实体。

var v = Ports.GroupJoin(Monitors, c => c.ComputerId, m => m.ComputerId, 
       (c, m) => new{c, m})
      .select(s => new {
           Computer = c.ComputerProp, 
           Monitors = m.Sum()});

如何添加第3个实体? 以下是一些可执行代码:http://dotnetfiddle.net/L1TBgJ

2 个答案:

答案 0 :(得分:1)

var v =
    from c in Computers

    // First outer join.
    join m in Monitors on c.ComputerId equals m.ComputerId into monitor
    from m in monitors.DefaultIfEmpty()

    // Second outer join.
    join p in Ports on m.PortId equals p.PortId into port
    from p in port.DefaultIfEmpty()

    // Group by the computers
    group new { m, p } by new { c } into g
    select new 
    {
        Computer = g.Key.c,
        Monitors = g.Select(i => i.m).Distinct().ToList(),
        Ports = g.Select(i => i.p).Distinct().ToList(),
    };

我认为这是你要找的东西。第二个连接意味着您需要确保对未包含在分组中的项目调用distinct。如果监视器对象上存在端口集合。然后你只需要嵌套查询的第二部分。

如果端口是显示器上的集合,并且您希望将它们展平以链接到计算机。我认为这样的事情应该这样做。

var v =
    from c in Computers

    // First outer join.
    join m in Monitors on c.ComputerId equals m.ComputerId into monitor
    from m in monitors.DefaultIfEmpty()

    // Group by the computers
    group new { m } by new { c } into g
    select new 
    {
        Computer = g.Key.c,
        Ports = g.Select(i => i.m).SelectMany(i => i.Port).ToList(),
    };

答案 1 :(得分:1)

由于您在EF模型中正确设置了关联,因此根本不需要连接。你应该能够这样做:

var v = Computers
        .Select(c => new {
             Computer = c.ComputerProp,
             Monitors = c.monitors.Count(), // note: not Sum(),
             Ports = c.monitors.Sum(m => m.ports.Count())
        });

我在您的测试脚本中注意到您正在使用对象,您没有实例化监视器和端口阵列并将它们设置为您添加的对象。如果你正在使用代码优先EF(我怀疑你是),那么在实际向数据库发出查询时将设置这些值。