Arvind Chourasiya
Arvind Chourasiya

Reputation: 17422

Check object null inside orderby Linq

I have one list with few records, list has another object inside it. Based on inner object I am using OrderByDescending. Inner object is null in few places so it is throwing NullReferenceException.

items.OrderByDescending(i=> i.Obj_Announcement.CreatedDate).ToList()

When Obj_Announcement is null the query throws an exception.

Not duplicate: I have object inside OrderByDescending not single field.

How can I solve this issue?

Upvotes: 0

Views: 1534

Answers (3)

Harald Coppoolse
Harald Coppoolse

Reputation: 30454

The neat method would be to separate your LINQ query from the way you define which item comes before another item. In other word: what is the sort order of Item X and Item Y

For this we need to create a class that implements IComparer<Item>

class Item
{
     public Announcement Announcement {get; set;} // your inner object, might be null
     ...
}

class Announcement
{
     public DateTime CreatedDate {get; set;}
     ...
}

Suppose you you have two Items, one of them has a null Anouncement.

Item item1 = new Item {Announcement = null};
Item item2 = new Item {Announcement = new AnnounceMent{CreatedDate=new DateTime(2000, 1, 1)}};

It's up to you to decide which comes first. Let's assume you want the null items at the end.

class ItemComparer : Comparer<Item>
{
     public static readonly ByCreatedDate = new ItemComparer();

     private static readonly IComparer<DateTime> dateComparer = Comparer<DateTime>.default;

     public override int CompareTo(Item x, Item y)
     {
         // TODO: decide what to do if x or y null: exception? comes first? comes last

         if (x.Announcement == null)
         {
             if (y.Announcement == null)
                 // x and y both have null announcement
                 return 0;
             else
                 // x.Announcement null, y.Announcement not null: x comes last:
                 return +1;
         }
         else
         {
             if (y.Announcement == null)
                 // x.Announcement not null and y.Announcement null; x comes first
                 return -1;
             else
                 // x.Announcement not null, y.Announcement not null: compare dates
                 return dateComparer.CompareTo(x.CreatedDate, y.CreateDate)
         }
    }
}

Usage:

(improved after comment by Peter Wurzinger)

var result = items.OrderByDescending(item => item, ItemComparer.ByCreatedDate);

Note: If you decide to change the sort order of your Items, all you have to do is change the ItemComparer class. All your LINQ statements that order your Items will then use the new sort order.

Upvotes: 3

iSpain17
iSpain17

Reputation: 3053

items.OrderByDescending(i=> i.Obj_Announcement?.CreatedDate ?? DateTime.MinValue).ToList()

should work, as if the value is null, it will return DateTime.MinValue (meaning null values will be at the back), and otherwise, you will just retrieve the CreatedDate.

Please note that I am not in front of a pc right now though, so could not test.

Upvotes: 1

Peter Wurzinger
Peter Wurzinger

Reputation: 411

You basically got two options depending on the resulting set you're expecting.

  1. Exclude the instances, which's Obj_Announcement - property is null. But imho that's not the scope of the question and would change the beavior of the program.

  2. Explicitly decide on how to deal with nulls.

When looking at the documentation for OrderByDescending (https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderbydescending) one comes across the keySelector parameter. Since it's a Func<TSource, TKey> it should be able to deal edge cases a TSource instance could show - for example nulls on a child-object, in your case. So imho the easiest solution on how to deal with that would be deciding whether you want to prepend or append the nulls to the list by

items.OrderByDescending(i=> i.Obj_Announcement?.CreatedDate ?? DateTime.MinValue).ToList()

or

items.OrderByDescending(i=> i.Obj_Announcement?.CreatedDate ?? DateTime.MaxValue).ToList()

But be aware, that DateTime.MinValue and DateTime.MaxValue are only assumptions as default values. They do not reflect the actual state of the object.

Upvotes: 2

Related Questions