首页 > 解决方案 > 具有空值的 C# IQueryable LINQ Group By

问题描述

我想根据一个属性从数据库中选择不同的值,并在该属性为null. IDs在我的数据库中是Strings

我的数据库如下所示:

Id1   Id2    Value
____________________
1     null   Value1
2     1      Value2
3     1      Value3
4     null   Value4
5     null   Value5
6     2      Value6
7     2      Value7
8     2      Value8

我想从我的查询中得到这样的输出:

Id1   Id2    Value
____________________
1     null   Value1
2     1      Value2  // i dont care which one from Id2 = 1 i get
4     null   Value4
5     null   Value5
6     2      Value6  // i dont care which one from Id2 - 2 i get

如您所见,我想获得一个List所有元素都Id2为空的元素,并且只返回一个Id2相同的元素(我不在乎哪个元素查询将返回)。

我试图编写一些代码:

query
   .Where(x => !string.IsNullOrEmpty(x.Id2))
   .GroupBy(z => z.Id2)
   .Select(grp => grp.FirstOrDefault())
   .ToListAsync();

但我没有得到我想要的,只有一个项目表示Id2,只有一个null值,像这样:

Id1   Id2    Value
____________________
1     null   Value1
2     1      Value2  // I want to get all elements where Id2 = null
6     2      Value6  // and distinct elements based on Id2

我的问题是,如何向 EF 编写查询以获取所有空项目和所有基于属性的不同项目?

标签: c#linqentity-framework-core

解决方案


在您的 GroupBy 之后,您有一个组序列。该组的 Key 是 Id2 的值。

如果 Key (Id2) 不为 null,则您只需要组中的一个元素,而不关心哪个元素。

如果 Key 等于 null,则需要该组的所有元素。

有两种方法可以做到这一点:

  • 对所有非空元素执行 GroupBy
  • 将它与空元素连接起来

或者:

  • 使用 GroupBy 的参数 ResultSelector 来检查 Key 是否为空,可以只选择一个元素,也可以选择该组的所有元素

连接方法

IQueryable<MyClass> source = dbContext....

// the elements with non-null Id2, keep only one element:
IQueryable<MyClass> filledId2 = source
    .Where(item => item.Id2 != null)
    .GroupBy(item => item.Id2,

        // Parameter ResultSelector, take Id2 (key) and all items with this Id2
        // to make one new:
        (key, itemsWithThisKey) => itemsWithThisKey.FirstOrDefault());

注意:不会有任何空组,因此结果中没有“默认”项目。

具有 null Id2 的元素:

IQueryable<MyClass> emptyId2 = source.Where(item => item.Id2 == null);

连接:

var result = filledId2.Concat(emptyId2);

注意:尚未执行任何查询。如果需要,您可以创建一个大的 LINQ 语句。这不会提高效率。然而,它会降低可读性。

结果参数方法

   IQueryable<MyClass> filledId2 = source.GroupBy(item => item.Id2,

       // resultSelector: if the key is null, select all elements of the group
       // otherwise select a sequence of one element of the group
       (key, itemsWithThisKey) => (key == null) ? 
             itemsWithThisKey : itemsWithThisKey.Take(1))

       // result: a sequence of sequences of MyClass objects
       // use SelectMany to make it one sequence of MyClass objects:
       .SelectMany(group => group);

推荐阅读