如何在子级可能为null时按层次结构CreatedOn进行排序

时间:2014-01-24 15:45:13

标签: c# linq collections

我查看了一些相关的问题,但没有看到任何与我需要的相符的东西,所以请耐心等待。

我有三个层次结构的类:

public class Dialog {
    public DateTime CreatedOn { get; set; }
}

public class Response {
    public DateTime      CreatedOn { get; set; }
    public IList<Dialog> Dialogs   { get; set; }
}

public class Request{
    public DateTime        CreatedOn { get; set; }
    public IList<Response> Responses { get; set; }
}

所以基本上,我正在显示请求,但我想按最近活动的顺序显示它们,无论是在消息,响应还是对话框上。显然,某些消息可能没有响应,而某些响应可能没有对话框。

我正在使用EntityFramework,但这并不一定相关,因为这个排序可以在任何使用LINQ来排序对象的分层分组上。

这是我到目前为止所做的事情:

Requests.OrderByDescending(r => r.CreatedOn)
        .ThenByDescending(r => r.Offers.Any() ? r.Offers.Max(o => o.CreatedOn) : DateTime.MinValue)
        .ThenByDescending(r => r.Offers.SelectMany(o => o.Dialogs).Any() ? r.Offers.SelectMany(o => o.Dialogs).Max(d => d.CreatedOn) : DateTime.MinValue)

我的问题是,我认为随后的ThenByDescending不会覆盖前一个,或者我是否以错误的方式思考这个问题?有没有办法在请求及其所有后代之间的一个订购中找到Max CreatedOn?

2 个答案:

答案 0 :(得分:1)

您可以获取每个列表的最大日期,然后获取tem的最大(最大?)日期,而不是进行多次排序(正如您所说的会破坏之前的排序)。

如果您至少保证有空的ResponseDialogs列表,则可以获得这样的最长日期:

 var r = req.OrderByDescending(x => new[]
 {
     x.CreatedOn,
     x.Responses.Select(y => y.CreatedOn).Max(),
     x.Responses.Select(y => y.Dialogs.Select(z => z.CreatedOn).Max()).Max()
 }.Max()).Select(x => x).ToList();

如果值可以为null,则可以在查询中创建新列表。如果您直接将此信息提供给EF来查询数据库,它可能无效......我现在无法在任何地方进行测试。

var r = req.OrderByDescending(x => new[]
{
    x.CreatedOn,
    (x.Responses ?? new List<Response>()).Select(y => y.CreatedOn).Max(),
    (x.Responses ?? new List<Response>()).Select(y => (y.Dialogs ?? new List<Dialog>())
                                .Select(z => z.CreatedOn).Max()).Max()
}.Max()).Select(x => x).ToList();

OP编辑

我必须做一些修改才能编译。正如基于这个答案的那样,感谢Grant Winney。

var results = req.OrderByDescending(r => new[] { // Create a new array to hold the dates.
                r.CreatedOn, // Start with the date of the Request
                (r.Offers.Any() ? 
                    r.Offers.Select(o => o.CreatedOn).Max() : // If the Request has any Offers, get the max date of the offers.
                    DateTime.MinValue), // Otherwise add Min value so it won't affect the results.
                (r.Offers.Any() ?
                    r.Offers.Select(o => o.Dialogs.Any() ? // If each Offer from each Request have any Dialogs
                        o.Dialogs.Select(d => d.CreatedOn).Max() : // Get the Max Dialog Date for each Offer
                        DateTime.MinValue) // Or put in a Min value for each Offer.
                    .Max() : // Then find the max for reach Offer's Dialogs (or Min Value for each Offer)
                    DateTime.MinValue) // If there are no Offers, again use Min value.
              }.Max()) // Then get the Max from the 3 values in the array.
              .Select(x => x).ToList();

答案 1 :(得分:1)

如果你不介意改变你的模型,也许你可以这样做:

public class Dialog {
  public DateTime CreatedOn { get; set; }
}

public class Response {
  public DateTime      CreatedOn { get; set; }
  public IList<Dialog> Dialogs   { get; set; }

  public DateTime MaxDateTime {
    get {
      if (Dialogs == null) return CreatedOn;

      var max = Dialogs.Max(o => o.CreatedOn);
      return CreatedOn >= max ? CreatedOn : max;
    }
  }
}

public class Request{
  public DateTime        CreatedOn { get; set; }
  public IList<Response> Responses { get; set; }

  public DateTime MaxDateTime {
    get {
      if (Responses == null) return CreatedOn;

      var max = Responses.Max(o => o.MaxDateTime);
      return CreatedOn >= max ? CreatedOn : max;
    }
  }
}

然后,您只需对MaxDateTime属性进行排序即可进行排序。

var orderedRequests = requests.OrderByDescending(o => o.MaxDateTime);
相关问题