【问题标题】:C# IQueryable LINQ Group By with null values具有空值的 C# IQueryable LINQ Group By
【发布时间】:2020-04-24 17:38:12
【问题描述】:

我想根据一个属性从数据库中选择不同的值,并在该属性为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 编写查询以获取所有空项和所有基于属性的不同项?

【问题讨论】:

  • 选择where not null distinct union where null?
  • 说实话,我不明白你在问什么:D 我准备了我想从我的查询中得到的示例输出

标签: c# linq entity-framework-core


【解决方案1】:

在您的 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());

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

Id2为空的元素:

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

连接:

var result = filledId2.Concat(emptyId2);

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

ResultParameter 方法

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

【讨论】:

    【解决方案2】:

    未经测试:

    var result = query.Where(x => x.Id2 == null || !query.Any(y => y.Id2 == x.Id2 && string.Compare(y.Id1, x.Id1) < 0))
    

    这应该会为您提供Id2 is null 的所有行,并且对于每组Id2,只有Id1 最小的行

    【讨论】:

    • Id1 和 Id2 是字符串
    • 所以您将数字作为字符串存储在名为Id 的列中?无论如何&lt; 比较也应该适用于字符串,对吧?
    • 是的,我将所有 id 存储为字符串(这就是为什么在我的代码 sn-p 中我写 string.IsNullOrEmpty())
    • Anyway &lt; comparison should work with strings too, right? - Not really.
    • 你为什么要用!Equals替换&lt;?最终用string.Compare(y.Id1, x.Id1) &lt; 0 替换它,就像GSerg 提到的问题的公认答案一样
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-13
    • 1970-01-01
    相关资源
    最近更新 更多