2017-04-26 52 views
1

我的查询返回匹配特定位置的人员列表。我希望它能够返回所有职位的列表,即使没有人匹配职位。我加入人们的原因是因为我需要“Assigned”的计数,这是一个与位置相匹配的动态添加计数字段。但是,如果有匹配谁,我希望它仍然返回驻扎位置的位置,但不想要的人“分配”设置为0。LINQ返回MVC5上的所有记录和groupby Razor查看

dynamic query = (from a in db.Positions 
        join b in db.People 
        on new { 
         a.GradeId, 
         a.SeriesId, 
         a.CompanyId, 
         a.PaybandId } 
        equals new { 
         b.GradeId, 
         b.SeriesId, 
         b.CompanyId, 
         b.PaybandId } into ab 
        from k in ab.DefaultIfEmpty() 
        join c in db.Grades on k.GradeId equals c.Id 
        join d in db.Series on k.SeriesId equals d.Id 
        join e in db.Companies on k.CompanyId equals e.Id 
        join p in db.Paybands on k.PaybandId equals p.Id 
        group a by new { CompanyName = e.Name, 
         GradeName = c.Name, 
         SeriesName = d.Name, 
         PaybandName = p.Name, 
         a.Authorized } into f 
        select new { Company = f.Key.CompanyName, 
         Grade = f.Key.GradeName, 
         Series = f.Key.SeriesName, 
         Payband = f.Key.PaybandName, 
         Authorized = f.Key.Authorized, 
         Assigned = f.Count() }).AsEnumerable().Select(r => r.ToExpando()); 

我面临的另一个问题是,剃刀页面上,该公司正在重复。我需要公司每个公司仅出现一次,但希望所有职位都列在其下。

这里是查看截图和剃刀脚本: Razor View Screenshot

@model IEnumerable<dynamic> 

@{ 
    Layout = "~/Views/Shared/_Layout.cshtml"; 
} 

<h2>@ViewBag.Title</h2> 
<table class="table table-responsive table-hover"> 
    <thead> 
     <tr> 
      <th class="col-sm-1"></th> 
      <th class="col-sm-2">Grade</th> 
      <th class="col-sm-2">Series</th> 
      <th class="col-sm-2">Payband</th> 
      <th class="col-sm-2">Authorized</th> 
      <th class="col-sm-2">Assigned</th> 
     </tr> 
    </thead> 
    @foreach (dynamic item in Model) 
    { 
     <thead> 
      <tr> 
       <th class="panel-bg" colspan="6">@item.Company</th> 
      </tr> 
     </thead> 
     <tbody> 
      <tr> 
       <td class="col-sm-1"></td> 
       <td class="col-sm-2">@item.Grade</td> 
       <td class="col-sm-2">@item.Series</td> 
       <td class="col-sm-2">@item.Payband</td> 
       <td class="col-sm-2">@item.Authorized</td> 
       <td class="col-sm-2">@item.Assigned</td> 
      </tr> 
     </tbody> 
    } 
</table> 

任何帮助,不胜感激!

EDIT(在请求添加Positions.cs类)

namespace CPR.Models 
{ 
using Newtonsoft.Json; 
    using System; 
    using System.Collections.Generic; 
    using System.ComponentModel.DataAnnotations; 
    using System.ComponentModel.DataAnnotations.Schema; 
    using System.Data.Entity.Spatial; 

    public partial class Positions 
    { 
     public Positions() 
     { 

     } 

     [Key] 
     public int Id { get; set; } 

     [Required] 
     public int CompanyId { get; set; } 

     [Required] 
     public int SeriesId { get; set; } 

     [Required] 
     public int GradeId { get; set; } 

     [Required] 
     public int PaybandId { get; set; } 

     [Required] 
     public int Authorized { get; set; } 

     public virtual Companies Companies { get; set; } 

     public virtual Series Series { get; set; } 

     public virtual Grades Grades { get; set; } 

     public virtual Paybands Paybands { get; set; } 
    } 
} 
+0

你可以添加你的职位类吗? –

+0

@MichaelBurns - 根据您的请求添加 – Element808

回答

2

你需要稍微改变逻辑您查询到更早计数,然后事情就会变得容易一点。请看下面的代码片段中的注释:

  ... 

      from a in db.Positions 
       join b in db.People 
       on new { 
        a.GradeId, 
        a.SeriesId, 
        a.CompanyId, 
        a.PaybandId } 
       equals new { 
        b.GradeId, 
        b.SeriesId, 
        b.CompanyId, 
        b.PaybandId } into ab 
       from k in ab.DefaultIfEmpty() 

       // you want to count the number of people per position here 

       group k by new { a.GradeId, a.SeriesId, a.CompanyId, a.PaybandId, a.Authorized } into g 
       select new { g.Key.GradeId, g.Key.SeriesId, g.Key.CompanyId, g.Key.PaybandId, g.Authorized, Count = g.Count(p => p != null) } into counts 

       // at this point you have position info AND people count per position 
       // next, you can do the remaining joins... 

       join c in db.Grades on counts.GradeId equals c.Id 
       join d in db.Series on counts.SeriesId equals d.Id 
       join e in db.Companies on counts.CompanyId equals e.Id 
       join p in db.Paybands on counts.PaybandId equals p.Id 

       // ... and select full data set, including person count, required for the view 

       select new { 
        CompanyName = e.Name, 
        GradeName = c.Name, 
        SeriesName = d.Name, 
        PaybandName = p.Name, 
        counts.Authorized, 
        Assigned = counts.Count 
       } into full 

       // one more step here that will help to tackle your second problem (grouping by company name in the view) 
       // you want your data coming into the view be grouped by company name 

       group full by full.CompanyName into groupByCompany 

       select new CompanyInfo { CompanyName = groupByCompany.Key, CompanyItems = groupByCompany.Select(i => new CompanyItem { GradeName = i.GradeName, SeriesName = i.SeriesName, PaybandName = i.PaybandName, Authorized = i.Authorized, Assigned = i.Assigned }).ToList() } 

       ... 

然后在视图中,您将需要创建2个@foreach循环,而不是一个。第一,外部之一,将循环在上面的收集和呈现的公司名称一排一次,而另一方面,内部一个,将在集合中的每个项目的CompanyItems财产环路和公司下呈现每件行名称行。

此外,由于有多个thead标签,它们是not allowed,因此您生成的HTML不是100%有效。以下是经过编辑的版本:

... 
@model IEnumerable<CompanyInfo> 

<table class="table table-responsive table-hover"> 
    <thead> 
     <tr> 
      <th class="col-sm-1"></th> 
      <th class="col-sm-2">Grade</th> 
      <th class="col-sm-2">Series</th> 
      <th class="col-sm-2">Payband</th> 
      <th class="col-sm-2">Authorized</th> 
      <th class="col-sm-2">Assigned</th> 
     </tr> 
    </thead> 
    @foreach (CompanyInfo item in Model) 
    { 
     <tbody> 
      <tr class="company-name"> 
       <th class="panel-bg" colspan="6">@item.CompanyName</th> 
      </tr> 
      @foreach (CompanyItem companyItem in item.CompanyItems) 
      { 
       <tr> 
        <td class="col-sm-1"></td> 
        <td class="col-sm-2">@companyItem.Grade</td> 
        <td class="col-sm-2">@companyItem.Series</td> 
        <td class="col-sm-2">@companyItem.Payband</td> 
        <td class="col-sm-2">@companyItem.Authorized</td> 
        <td class="col-sm-2">@companyItem.Assigned</td> 
       </tr> 
      } 
     </tbody> 
    } 
</table> 

... 

要使用强类型的视图模式,添加这两个类:

class CompanyInfo 
{ 
    public string CompanyName { get; set; } 
    public List<CompanyItem> CompanyItems { get; set; } 
} 

class CompanyItem 
{ 
    public string GradeName { get; set; } 
    public string SeriesName { get; set; } 
    public string PaybandName { get; set; } 
    public bool Authorized { get; set; } 
    public int Assigned { get; set; } 
} 

,然后更新代码按照上述更新。

+0

令人惊叹的,彻底的答案。随着你的实现,我收到了一些错误。 1.名称'a'在当前上下文中不存在,2.'AnonymousType#1'不包含'Count'的定义。选择新的{ 公司名称= e.Name, GradeName = c.Name, SERIESNAME = d.Name, PaybandName = p.Name, a.Authorized,// <----错误1 分配=计数。 Count()// <---- error 2 }全部变为 – Element808

+1

对不起,您是对的。修正它们,请参阅更新的代码。基本上,'Assigned = counts.Count()'应该是'Assigned = counts.Count';对于'a.Authorized',它应该被包含到第一组中并且从那里被重用。此外,请注意在视图中使用'class =“company-name”'attr。自从我们摆脱了多个'thead's之后,需要用CSS对公司名称行进行样式设计。 – m1kael

+0

在视图上最后一件事是用代码替换页面上的所有内容并传递到视图@model IEnumerable 。我经历了所有事情,并且在动态查询中选择了最终选项,它应该包含“CompanyName”,但是获取错误:'object'不包含视图上'CompanyName'的定义。有任何想法吗? – Element808

-1

“我希望它返回所有位置的列表,即使没有 人谁的位置相匹配。”

如你所说,你应该简单地做下一个(编辑这一部分):

前:

from a in db.Positions 
join b in db.People 

后:

from a in db.Positions 
left join b in db.People 

正如你所看到的我申请了left join,因为SQL LEFT JOIN返回左侧的所有行表,即使右表没有匹配。这意味着如果ON子句匹配右表中的0(零)记录;该连接仍然会返回结果中的一行,但右表中的每列都有NULL。

这意味着左连接返回左表中的所有值,以及来自右表的匹配值,或者在没有匹配连接谓词的情况下返回NULL。

我希望这有助于你的, 干杯

+2

“左连接”不是有效的LINQ语法,不幸的是,否则这将更简单:) – Element808