1

I have following class structure and I need to find the most recent UpdatedDate for Order i.e. Max of Order / Orderline / OrderlineSize / OrderlineVAS. The lists are never null but might be empty.

class Order
{
    public List<Orderline> Orderlines { get; set; }
    public DateTime UpdatedDate { get; set; }
}

class Orderline
{
    public List<OrderlineSize> OrderLineSizes { get; set; }
    public List<OrderlineVAS> OrderLineValueAddedServices { get; set; }
    public DateTime UpdatedDate { get; set; }
}
class OrderlineSize
{
    public DateTime UpdatedDate { get; set; }
}

class OrderlineVAS
{
    public DateTime UpdatedDate { get; set; }
}

I am using the following code to get Max which works fine if there are some OrderLineSizes but throws a NullReferenceException when list is empty.

(new List<DateTime?>() {
    order.UpdatedDate,
    order.Orderlines.Max(x=>x.UpdatedDate),
    order.Orderlines.SelectMany(x=>x.OrderLineSizes).DefaultIfEmpty()?.Max(y=>y.UpdatedDate),
    order.Orderlines.SelectMany(x=>x.OrderLineValueAddedServices).DefaultIfEmpty()?.Max(y=>y.UpdatedDate)
})
.Max().Value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");

I have solved it using elvis operation from C# 6 as below

(new List<DateTime?>() {
    order.UpdatedDate,
    order.Orderlines.Max(x=>x?.UpdatedDate),
    order.Orderlines.SelectMany(x=>x.OrderLineSizes).Max(y=>y?.UpdatedDate),
    order.Orderlines.SelectMany(x=>x.OrderLineValueAddedServices).Max(y=>y?.UpdatedDate)
})
.Max().Value.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ")

How could it be solved using earlier versions of C#? I reckon the use of Select clause in Linq but not sure if that is a good option. How can I optimize it for better performance?

  • `Select` is good enough option. `.Select(x => x.UpdatedDate).DefaultIfEmpty().Max()` – Evk Apr 02 '18 at 09:13

1 Answers1

1

The exception is being thrown from:

.DefaultIfEmpty()?.Max(y=>y.UpdatedDate)

DefaultIfEmpty does not return null. Rather, it returns an enumerable with a single null entry. Therefore it passes through the ?. to Max. Finally the null is processed at y.UpdateDate with y == null, causing the NullReferenceException.

Resolve by removing the ? from after DefaultIfEmpty, and instead add it after y as follows:

.DefaultIfEmpty().Max(y=>y?.UpdatedDate)

This needs to be done in both lines that this pattern appears.

Also, the whole thing can be made more efficient and succinct with:

        string result =                
            Enumerable.Repeat(order.UpdatedDate, 1)
                .Concat(order.Orderlines.Select(x1 => x1.UpdatedDate))
                .Concat(order.Orderlines.SelectMany(x => x.OrderLineSizes).Select(x => x.UpdatedDate))
                .Concat(order.Orderlines.SelectMany(x => x.OrderLineValueAddedServices).Select(x => x.UpdatedDate))
                .Max().ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ");
Peter Aylett
  • 750
  • 3
  • 8