У меня есть один list с несколькими записями, внутри list есть еще один object. Основываясь на внутреннем объекте, я использую OrderByDescending. Внутренний объект находится null в нескольких местах, поэтому он бросает NullReferenceException.
items.OrderByDescending(i=> i.Obj_Announcement.CreatedDate).ToList()
Когда Obj_Announcement равно null, запрос выдает исключение.
Не дублировать: у меня есть объект внутри OrderByDescending не одно поле.
Как я могу решить эту проблему?
Чтобы решить эту проблему, вы должны сначала решить, как следует сортировать значения null. Должны ли они быть самыми низкими или самыми высокими?
@RenéVogt - Мне нужны последние записи сверху, вот так.
Сетевая библиотека не допускает null для объекта DateTime. Обычно я использую новый DateTime, который устанавливает дату по умолчанию на 1/1/1. Затем я проверяю значение null, проверяя, больше ли дата 1/1/1.
Вы должны заказать hasValue внутреннего объекта, а затем упорядочить список по его значению: items.OrderByDescending(i=> i.Obj_Announcement.CreatedDate.HasValue).ThenByDescending(i=> i.Obj_Announcement.CreatedDate.Value)





items.OrderByDescending(i=> i.Obj_Announcement?.CreatedDate ?? DateTime.MinValue).ToList()
должно работать, как если бы значение было нулевым, оно вернет DateTime.MinValue (это означает, что нулевые значения будут сзади), а в противном случае вы просто получите CreatedDate.
Обратите внимание, что я сейчас не перед компьютером, поэтому не могу проверить.
В основном у вас есть два варианта в зависимости от ожидаемого набора.
Исключите экземпляры, которые Obj_Announcement - свойство равно null. Но imho это не входит в объем вопроса и изменит поведение программы.
Явно решите, как поступать с нулями.
При просмотре документации для OrderByDescending (https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderbydescending) можно встретить параметр keySelector. Поскольку это Func<TSource, TKey>, он должен иметь возможность обрабатывать крайние случаи, которые может показать экземпляр TSource - например, нули в дочернем объекте, в вашем случае.
Так что imho самое простое решение о том, как с этим справиться, - решить, хотите ли вы добавлять или добавлять нули в список с помощью
items.OrderByDescending(i=> i.Obj_Announcement?.CreatedDate ?? DateTime.MinValue).ToList()
или
items.OrderByDescending(i=> i.Obj_Announcement?.CreatedDate ?? DateTime.MaxValue).ToList()
Но имейте в виду, что DateTime.MinValue и DateTime.MaxValue являются только предположениями в качестве значений по умолчанию. Они не отражают фактического состояния объекта.
Аккуратным методом было бы отделить ваш запрос LINQ от того, как вы определяете, какой элемент предшествует другому элементу. Другими словами: каков порядок сортировки элемента X и элемента Y.
Для этого нам нужно создать класс, реализующий IComparer<Item>
class Item
{
public Announcement Announcement {get; set;} // your inner object, might be null
...
}
class Announcement
{
public DateTime CreatedDate {get; set;}
...
}
Предположим, у вас есть два Items, один из них имеет нулевой Anouncement.
Item item1 = new Item {Announcement = null};
Item item2 = new Item {Announcement = new AnnounceMent{CreatedDate=new DateTime(2000, 1, 1)}};
Вам решать, что на первом месте. Предположим, вам нужны нулевые элементы в конце.
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)
}
}
}
Использование:
(улучшено после комментария Питера Вурцингера)
var result = items.OrderByDescending(item => item, ItemComparer.ByCreatedDate);
Примечание. Если вы решите изменить порядок сортировки своих элементов, все, что вам нужно сделать, это изменить класс ItemComparer. Все ваши операторы LINQ, которые упорядочивают ваши элементы, будут использовать новый порядок сортировки.
+1, это отличный ответ! Но думаю перегрузки для OrderByDescending(IEnumerable<TSource>, IComparer<TSource>) по docs.microsoft.com/en-us/dotnet/api/… нет. Имхо, поэтому вызов должен выглядеть как items.OrderByDescending(item => item, ItemComparer.ByCreatedDate); И следует отметить, что он, скорее всего, не переводится при использовании с IQueryable<TSource>, например, OR-mapping, OData или чем-то еще.
Возможный дубликат Порядок LINQ по нулевому столбцу, где порядок восходящий, а нули должны быть последними