如何在嵌套表字段上linq OrderBy?

时间:2017-09-29 16:11:51

标签: c# .net linq linq-to-sql sql-order-by

我有一份报告清单,每份报告都有一些“网络”。这些“网队”中的每一个都有一个状态,我想根据他们可能拥有的状态重新排序这些报告。我正在错误地编写LINQ表达式,因为我的OrderBy()括号中的内容不是可订购的实际单个字段。这是它的外观:

reports.OrderBy(r => r.Nets.Where(n => n.NetStatus.Equals("New")).OrderBy(n => n.NetId))
    .ThenBy(r => r.Nets.Where(n => n.NetStatus.Equals("Updated")).OrderBy(n => n.NetId))
    .ThenBy(r => r.Nets.Where(n => n.NetStatus.Equals("Ignored")).OrderBy(n => n.NetId))

因为它不是OrderBy和ThenBy子句中的实际可订购字段,所以会抛出一个错误,说“DbSortClause表达式必须具有类似于命令的类型参数Name:Key”,这显然是有道理的。我无法弄清楚的是如何正确地传达我正在尝试订购的内容,如第一段所述。

5 个答案:

答案 0 :(得分:1)

您可以尝试将顶层Linq语句分解为foreach,并为您拥有的每种NetStatus类型创建一个列表。然后,您可以将列表组合成订购结果。

foreach (var r in reports)
    {
        var newList = r.Nets.Where(n => n.NetStatus.Equals("New")).OrderBy(n => n.NetId).ToList();

        var upatedList = r.Nets.Where(n => n.NetStatus.Equals("Updated")).OrderBy(n => n.NetId).ToList();

        var IgnoredList = r.Nets.Where(n => n.NetStatus.Equals("Ignored")).OrderBy(n => n.NetId).ToList();

        var result = newList.Concat(upatedList).Concat(IgnoredList);
    }

建立上述代码;按Reports的顺序对Reports进行排序,其中包含至少1个&#39;新&#39; NetStatus,然后Reports包含至少1 Updated个NetStatus,然后是剩余的Reports,我们可以根据每个r对象将每个Status List对象添加到一个独立的列表中我们在上面创建的 var sortedReports = new List<Report>(); foreach (var r in reports) { var newList = r.Nets.Where(n => n.NetStatus.Equals("New")).OrderBy(n => n.NetId).ToList(); var upatedList = r.Nets.Where(n => n.NetStatus.Equals("Updated")).OrderBy(n => n.NetId).ToList(); var IgnoredList = r.Nets.Where(n => n.NetStatus.Equals("Ignored")).OrderBy(n => n.NetId).ToList(); if (newList.Count > 0) { sortedReports.Add(r); } else if (upatedList.Count > 0) { sortedReports.Add(r); } else { sortedReports.Add(r); } } 类型:

cv2.aruco

答案 1 :(得分:1)

  1. 按网络状态填写并排序netStatusList列表(这是为了帮助您进行自定义排序)。
  2. 将您的报告展平为一个网络列表。
  3. netStatusList中的每个净状态索引排序,然后按网络ID排序。

    List<String> netStatusList= new List<String> { "New",  "Updated", "Ignored"  };
    
    reports.SelectMany(r => r.Nets).ToList().OrderBy(n => netStatusList.IndexOf(n.NetStatus))
    .ThenBy(n => n.NetId);
    

答案 2 :(得分:1)

根据your comment关于当前答案之一(这应该是问题的一部分):

  

更像是,报告至少有一个状态为&#34; New&#34;,先排序。然后&#34;更新&#34;,然后&#34;忽略&#34;。现在关于所有报告如何与&#34; New&#34;应该分类,它真的不重要;也许按他们的报告排序。

您正在寻找某种优先顺序

要实现这一点,您可以使用条件表达式(如

)为每个状态分配优先级
status == "New" ? 1 :
status == "Updated" ? 2 :
status == "Ignored" ? 3 : 4

应转换为SQL CASE WHEN ... THEN ... WHEN ... THEN ... ELSE .. END表达式。

此外,由于您可能具有多种状态,因此您可以使用Min函数来确定主记录的优先级,然后将其用于排序。

付诸行动(DefaultIfEmpty()将处理空r.Nets):

var query = reports
    .OrderBy(r => r.Nets.DefaultIfEmpty().Min(n => 
         n.NetStatus == "New" ? 1 :
         n.NetStatus == "Updated" ? 2 :
         n.NetStatus == "Ignored" ? 3 :
         4))
    .ThenBy(r => r.ReportId);

或者如果查询提供程序不处理透明的null记录(如EF提供程序那样):

var query = reports
    .OrderBy(r => r.Nets.Select(n => n.NetStatus).DefaultIfEmpty().Min(status => 
         status == "New" ? 1 :
         status == "Updated" ? 2 :
         status == "Ignored" ? 3 :
         4))
    .ThenBy(r => r.ReportId);

无论哪种方式都有效并且翻译得更好。

答案 3 :(得分:0)

如果您的目标是获得每个报告的网队

reports.SelectMany(r => r.Nets).GroupBy(e=> e.NetStatus).Select(e=> new 
{
   Key = e.Key,
   List = e.OrderBy(e=> e.NetId)
});

然而,如果你是报告,那么你可以

reports.Select(report => new 
{
    Report = report,
    Nets = report.Nets.GroupBy(e=> e.NetStatus).Select(e=> new {
               Key = e.Key,
               List = e.OrderBy(e=> e.NetId)
           });
});

答案 4 :(得分:-1)

我认为您可能误解了OrderBy的工作方式。您没有提供首先要订购的商品,而是提供value订购。因此,在您的情况下,您希望在NetStatus上订购,但是,如果您刚刚reports.OrderBy(r => r.NetStatus),则会在NetStatus上按字母顺序排序。

相反,您需要根据实际需要排序的顺序返回一个正确排序的值。考虑这样的事情:

reports.OrderBy(r => {
switch(r.NetStatus){
case "New": return 1;
case "Updated": return 2;
case "Ignored": return 3;
}
})

现在,您在NetStatus的值上返回值1,2或3 基于,但是,1,2和3将正确排序。 然后,您可以使用.ThenBy(n => n.NetId)对其进行跟进,以便按NetStatus字段排序后的NetId字段进行排序。

编辑:IvanStoev在下面指出NetStatus实际上是Net集合中的一个属性,而不是直接报告。您可能需要调整我的示例以进行补偿,但希望它可以解决这个问题。您需要为OrderBy提供单个单独值以进行排序,而不是值集合。

完整示例:

 class Program {
    static void Main(string[] args) {
      List<Report> reports = new List<Report>();
      reports.Add(new Report() { NetStatus = "Updated", NetId = 1 });
      reports.Add(new Report() { NetStatus = "New", NetId = 2 });
      reports.Add(new Report() { NetStatus = "Ignored", NetId = 4 });
      reports.Add(new Report() { NetStatus = "Ignored", NetId = 3 });

      var orderedReports = reports.OrderBy((r) => {
        switch (r.NetStatus) {
          case "New": return 1;
          case "Updated": return 2;
          case "Ignored": return 3;
          default: return 0;
        }
      }).ThenBy(r => r.NetId);

      foreach (var report in orderedReports) {
        Console.WriteLine($"{report.NetId} {report.NetStatus}");
      }
      Console.ReadLine();
    }
  }
  public class Report {
    public String NetStatus { get; set; }
    public int NetId { get; set; }
  }

输出

2 New
1 Updated
3 Ignored
4 Ignored