LINQ GroupBy,同时保留所有对象字段

4

我目前有这个数据表的示例:

ID  | Policy ID     |   History ID  | Policy name
1   |   1           |    0          | Test
2   |   1           |    1          | Test
3   |   2           |    0          | Test1
4   |   2           |    1          | Test1

基于此,我想按照政策ID和历史ID(最大值)进行分组,因此我想保留的记录是ID为2和4的记录:

   ID   | Policy ID     |   History ID  | Policy name
    2   |   1           |    1          | Test
    4   |   2           |    1          | Test1

我尝试使用LINQ完成这个任务,但每次都遇到同样的问题。我可以对实体进行分组,但总是得重新定义属性,而不能从我的Policy对象中保留它们。例如:

var policies = _context.Policies.GroupBy(a => a.intPolicyId)
                                            .Select(group => new {
                                                PolicyID = group.Key,
                                                HistoryID = group.Max(a => a.intHistoryID)
                                            });

这只是列出了具有“策略ID”和“历史记录ID”的对象列表。我想要从Policies对象返回所有属性,而不必重新定义它们,因为该对象中有大约50个以上的属性。
        var policies = _context.Policies.GroupBy(a => a.intPolicyId)
                                                    .Select(group => new {
                                                        PolicyID = group.Key,
                                                        HistoryID = group.Max(a => a.intHistoryID)
                                                        PolicyObject = group;
                                                    });

但是这个出错了。

有什么想法吗?


我有点困惑:如果intPolicyID是Policies表的主键,为什么要按它进行分组?这将始终返回表中的每一行,因为每个intPolicyID都是唯一的。 - Richthofen
我认为这只是我自己的绝望编码失误,为了让它工作。实际上,我猜每一行只能按历史ID分组。 - Chris Dixon
好的。在这种情况下,您也不能按历史ID分组。不确定您是否有客户ID或其他字段需要真正分组。但这是此过程中的第一步。 - Richthofen
重新阅读您的问题,我认为您想要每个策略的最新“历史”对象,并且您假设每个策略的最大历史ID是最近的。但是,历史记录项是否甚至保存在策略表中?很可能它们保存在Histories表或类似的表中。您应该使用_context.Histories.GroupBy(a => a.intPolicyId),因为intPolicyID可以在历史记录表中出现多次。这只是一个猜测,因为我看不到您的模式。 - Richthofen
哦,实际上,你可能有一个能够加速查询的想法,我也会尝试实现这种方法并检查速度... - Chris Dixon
2个回答

3

按组合键分组

_context.Policies.GroupBy(a => new {a.intPolicyId, *other fields*}).Select(
    group=> new {
        PolicyId = group.Key.intPolicyId,
        HistoryId = group.Max(intHistoryId),
        *other fields*
    }
);

另一种方法是提取历史记录,然后将其与其他数据连接起来,就像这样(无法直接使用,需要进行一些精细调整)。
var historyIDs = _context.Policies.GroupBy(a=>a.intPolicyId).Select(group => new {
                                            PolicyID = group.Key,
                                            HistoryID = group.Max(a => a.intHistoryID)
                                        });

var finalData = from h in historyIDs
                join p in _context.Policies on h.intPolicyId equals p.intPolicyId
                select new {h.HistoryId, *all other policy fields*}

还有一种更简单的方式,不需要大量输入 :):

var historyIDs = _context.Policies.GroupBy(a=>a.intPolicyId).Select(group => new {
                                            PolicyID = group.Key,
                                            HistoryID = group.Max(a => a.intHistoryID)
                                        });

var finalData = from h in historyIDs
                join p in _context.Policies on h.PolicyId equals p.intPolicyId && h.HistoryId equals p.HistoryId
                select p

基本上它相当于以下 SQL 查询:
select p.*
from Policy p
inner join (
    select pi.policyId, max(pi.historyId)
    from Policy pi
    group by pi.policyId
) pp on pp.policyId = p.policyId and pp.historyId = p.historyId

1
谢谢你的回答,但这不是和我现在已经拥有的一样吗?我必须输入其他字段(因此需要50多个属性)才能使其工作? - Chris Dixon
第二个选项需要返回到数据库,因此在一个方法中需要进行2次往返。非常感谢您的帮助,但肯定有一种方法可以在一次往返中完成这个操作吧? - Chris Dixon
不太对。第一个查询不会立即执行,而只是嵌入到第二个查询的查询树中。因此,它确实应该是对数据库的单次往返。 - J0HN
我检查了你的代码,认为这可能有效 - 5分钟后会回报! - Chris Dixon
只要您不枚举historyIDs变量,它就不会构建/运行查询。因此性能不应受影响;数据库查询将在调用.ToList()或foreach on finalData/historyIDs时发生。 - Richthofen

0
在LINQ to Objects中,我会这样做:
var policies = _context.Policies
    .GroupBy(a => a.intPolicyId)
    .Select(g => g.OrderByDescending(p => p.intHistoryID).First());

但是你的_context暗示可能涉及到数据库,我不确定这个是否能被翻译。

基本上,它按照策略ID分组,然后在每个组内按照历史ID进行排序,并从每个组中选择具有最高历史ID的行。它返回与Policies中相同的类型。


谢谢您的回复,但我在让这个解决方案工作时遇到了问题。错误是查询无法编译,并收到错误消息:“由于先前的函数评估超时,因此禁用了函数评估。您必须继续执行以重新启用函数评估。”,这是在.FirstOrDefault()上的,.First()完全失败。 - Chris Dixon
很奇怪,我本来以为这会起作用的 :( 你可以尝试将 OrderBy 移动到 GroupBy _之前_,但理论上不应该有太大的区别。 - Rawling
超时发生的原因很可能是由于大型数据集/表。基本上,对于表中的每一行,您都会使用.First()调用再次查询数据库。.First()调用强制对GroupBy/Select链中的每个对象进行评估,这对任何实质性的性能都是毁灭性的。 - Richthofen
@Richthofen 如果是这样的话,我希望LINQ提供程序能够更好地处理它;我希望OrderBy...First部分被包含为子查询,而不是一批后续查询。 - Rawling

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接