2012-12-19 89 views
6

我公司有0到n个部门,1个部门有0到n个办公室,1个办公室有0到n个办公室。现在我需要一个使用linq来查询雇员平均年龄的查询部门,如果没有人在一个部门则默认平均为0。 代码如下:使用LINQ到实体的外部联接查询

DataContext ctx = new DataContext(); 

    var q0 = from d in ctx.Departments 
      join o in ctx.Offices on d.Id equals o.DepartmentId 
      join e in ctx.Employees on o.Id equals e.OfficeId 
      group e by d into de 
      select new { 
       DepartmentId = de.Key.Id, 
       AverageAge = de.Count() == 0 ? 0 : de.Average(e => e.Age), 
      }; 


    var q1 = from d in ctx.Departments 
      join de in q0 on d.Id equals de.DepartmentId into des 
      from de in des.DefaultIfEmpty() 
      select new 
      { 
       DepartmentName = d.Name, 
       AverageAge = de == null ? 0 : de.AverageAge 
      }; 

    var result = q1.ToList(); 
    foreach (var item in result) 
    { 
     Console.WriteLine("{0}-{1}", item.DepartmentName, item.AverageAge); 
    } 
    ctx.Dispose(); 

但如何Q0和Q1结合起来,一个查询?

+2

下面是关于制定好文章与LINQ联接:http://www.codeproject.com/Articles/488643/LinQ-Extended-Joins –

+0

完美的扩展。但他们是方法,而不是流利的linq。 – user1729842

回答

7
were you meaning something along the lines of: 

var newQ2 = from d in ctx.Departments 
       outer left join o in ctx.Offices on d.Id equals o.DepartmentId 
       outer left join e in ctx.Employees on o.Id equals e.OfficeId 
       group e by d into de 
       select new { 
        DepartmentId = de.Key.Id, 
        AverageAge = de.Count() == 0 ? 0 : de.Average(e => e.Age), 
       }; 

改为:

var newQ2 = from d in ctx.Departments 
        join o in ctx.Offices on d.Id equals o.DepartmentId 
        join e in ctx.Employees on o.Id equals e.OfficeId 
        group e by d into de.DefaultIfEmpty() 
        select new { 
         DepartmentId = de.Key.Id, 
         DepartdentName = select d.Name from d where d.id = de.Key.Id, 
         AverageAge = de.Count() == 0 ? 0 : de.Average(e => e.Age), 
        }; 

附录:我会用一个子选择匹配了额外的名字,不知道你的数据库布局我从你的代码即兴的,但你可以使它更有效率,并且还有基于子选择的多部分连接。对不起,我不能在工作中测试这个代码,我可以近似相当好,但如果您需要更详细的答案,需要更多关于您的部门名称所在位置的信息:)我已将外部左连接更改回连接,对不起我忘记了在LINQ的C#中,你可以使用DefaultIfEmpty()在代码中引起外部左连接行为。

在没有相应值的情况下,外部左连接将返回空值,但将允许具有相应值的任何部分返回。但是,加入不会返回任何空的条目,我怀疑这是为什么你有两个查询?

我提出的查询中唯一需要注意的是,在使用它们之前,如果它们是空值,则需要填充所需的任何值,例如,DepartmentId在DE为空时需要一些逻辑来填充它。

+0

我想你可以修改这个稍微拉一点名字部分,然后我相信OP的第二个查询是不必要的。 – pstrjds

+2

外部左连接是一个很好的解决方案。但它不是有效的linq语法,如果语法有效,那么3个单词将作为c#关键词蓝色。 – user1729842

0

谢谢大家,我已经得到了答案:

 var q1 = 
       from d in ctx.Departments 
       from o in ctx.Offices.Where(o => o.DepartmentId == d.Id).DefaultIfEmpty() 
       from e in ctx.Employees.Where(e => e.OfficeId == o.Id).DefaultIfEmpty() 
       group e by d into de 
       select new { 
        DepartmentName = de.Key.Name, 
        AverageAge = de.Average(e => e == null ? 0 : e.Age), 
       };