【问题标题】:Check object null inside orderby Linq在 orderby Linq 中检查对象 null
【发布时间】:2023-03-23 07:14:01
【问题描述】:

我有一个 list 的记录很少,list 里面还有另一个 object。基于我使用的内部对象OrderByDescending。内部对象在少数地方是null,所以它正在抛出NullReferenceException

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

Obj_Announcementnull 时,查询会引发异常。

不重复:我在 OrderByDescending 内有对象,而不是单个字段。

我该如何解决这个问题?

【问题讨论】:

  • 要解决这个问题,您首先应该决定null 值应该如何排序。它们应该是最低的还是最高的?
  • @RenéVogt - 我需要最新记录,这样我需要。
  • 网络库不允许 DateTime 对象为空。通常我使用一个新的 DateTime,它将默认日期设置为 1/1/1。然后我通过检查日期是否大于 1/1/1 来测试 null。
  • 您应该订购内部对象的 hasValue,然后按其值订购列表:items.OrderByDescending(i=> i.Obj_Announcement.CreatedDate.HasValue).ThenByDescending(i=> i.Obj_Announcement.CreatedDate.Value)

标签: c# linq


【解决方案1】:

简洁的方法是将您的 LINQ 查询与定义哪个项目在另一个项目之前的方式分开。换句话说:Item X和Item 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)}};

由您决定哪个先出现。让我们假设您想要最后的 null 项。

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)
         }
    }
}

用法:

(Peter Wurzinger 评论后改进)

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

注意:如果您决定更改项目的排序顺序,您所要做的就是更改 ItemComparer 类。然后,对您的项目进行排序的所有 LINQ 语句都将使用新的排序顺序。

【讨论】:

  • +1,这是一个很好的答案!但根据docs.microsoft.com/en-us/dotnet/api/…,我认为OrderByDescending(IEnumerable&lt;TSource&gt;, IComparer&lt;TSource&gt;) 没有过载。因此,恕我直言,调用应该看起来像 items.OrderByDescending(item =&gt; item, ItemComparer.ByCreatedDate); 并且应该注意的是,当与 IQueryable&lt;TSource&gt; 一起使用时,它很可能是不可翻译的,例如 OR-mapping、OData 或其他。
【解决方案2】:

您基本上有两个选项,具体取决于您期望的结果集。

  1. 排除实例,即Obj_Announcement - 属性为空。但恕我直言,这不是问题的范围,会改变程序的行为。

  2. 明确决定如何处理空值。

查看OrderByDescending (https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderbydescending) 的文档时,会遇到keySelector 参数。由于它是Func&lt;TSource, TKey&gt;,它应该能够处理TSource 实例可能显示的边缘情况 - 例如,在您的情况下,子对象上的空值。 因此,恕我直言,关于如何处理该问题的最简单解决方案是决定是否要通过

将空值添加到列表中或添加到列表中
items.OrderByDescending(i=> i.Obj_Announcement?.CreatedDate ?? DateTime.MinValue).ToList()

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

但请注意,DateTime.MinValueDateTime.MaxValue 只是作为默认值的假设。它们不反映对象的实际状态。

【讨论】:

    【解决方案3】:
    items.OrderByDescending(i=> i.Obj_Announcement?.CreatedDate ?? DateTime.MinValue).ToList()
    

    应该可以,如果值为null,它将返回DateTime.MinValue(意味着null值将在后面),否则,您将只检索CreatedDate。

    请注意,我现在不在电脑前,所以无法测试。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多