Проверить объект null внутри порядка Linq

У меня есть один list с несколькими записями, внутри list есть еще один object. Основываясь на внутреннем объекте, я использую OrderByDescending. Внутренний объект находится null в нескольких местах, поэтому он бросает NullReferenceException.

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

Когда Obj_Announcement равно null, запрос выдает исключение.

Не дублировать: у меня есть объект внутри OrderByDescending не одно поле.

Как я могу решить эту проблему?

Чтобы решить эту проблему, вы должны сначала решить, как следует сортировать значения null. Должны ли они быть самыми низкими или самыми высокими?

René Vogt 18.02.2019 10:33

@RenéVogt - Мне нужны последние записи сверху, вот так.

R15 18.02.2019 10:40

Сетевая библиотека не допускает null для объекта DateTime. Обычно я использую новый DateTime, который устанавливает дату по умолчанию на 1/1/1. Затем я проверяю значение null, проверяя, больше ли дата 1/1/1.

jdweng 18.02.2019 11:02

Вы должны заказать hasValue внутреннего объекта, а затем упорядочить список по его значению: items.OrderByDescending(i=> i.Obj_Announcement.CreatedDate.HasValue).ThenByDescending(i=‌​> i.Obj_Announcement.CreatedDate.Value)

Muhammad Vakili 18.02.2019 11:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
5
322
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

должно работать, как если бы значение было нулевым, оно вернет DateTime.MinValue (это означает, что нулевые значения будут сзади), а в противном случае вы просто получите CreatedDate.

Обратите внимание, что я сейчас не перед компьютером, поэтому не могу проверить.

Ответ принят как подходящий

В основном у вас есть два варианта в зависимости от ожидаемого набора.

  1. Исключите экземпляры, которые Obj_Announcement - свойство равно null. Но imho это не входит в объем вопроса и изменит поведение программы.

  2. Явно решите, как поступать с нулями.

При просмотре документации для 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 или чем-то еще.

Peter Wurzinger 18.02.2019 12:27

Другие вопросы по теме