LINQ返回MVC5 Razor View上的所有记录和groupby

时间:2017-04-26 21:25:17

标签: c# asp.net-mvc linq razor

我的查询会返回与特定排名匹配的人员列表。我希望它返回所有位置的列表,即使没有人匹配该位置。我加入别人的原因是因为我需要计算"已分配"这是一个与位置匹配的动态添加计数字段。但是,如果没有人匹配这个位置,我希望它仍然可以返回这个位置,但是想要" Assigned"设置为0。

    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>

非常感谢任何帮助!

编辑(根据要求添加了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; }
    }
}

2 个答案:

答案 0 :(得分: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; }
}

然后根据上面的更新更新代码。

答案 1 :(得分:-1)

  

&#34;我希望它返回所有职位的清单,即使没有   匹配职位的人。&#34;

正如您所说,您应该只做下一步(编辑此部分):

之前:

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。

我希望这对你有帮助, 干杯