首页 > 解决方案 > 使用 LINQ 选择子查询的 SUM()

问题描述

我正在尝试学习如何使用 LINQ 执行产生与此相同结果的查询:

SELECT (
  SELECT SUM(point)
  FROM communitymemberpointfeature
  WHERE communitymemberpointfeature.communitymemberid = communitymember.id
) AS points, communitymember.*
FROM communitymember

在网上浏览了一下之后,我构造了以下语句:

var list = (from pointFeature in communityMemberPointFeatureList
    join member in communityMemberList on pointFeature.CommunityMemberId equals member.Id
    group pointFeature by new { pointFeature.CommunityMemberId }
    into grouping
    select new
    {
        grouping,
        points = grouping.Sum(row => row.Point)
    }).ToList();

但这产生了类似的结果

[
  {
    points:7200,
    grouping:[
        {Id:1,Point:5000,FeatureId:1,CommunityMemberId:1},
        {Id:2,Point:2200,FeatureId:1,CommunityMemberId:1},
    ],
  }
  ...
]

我真正想要的是一个结果集,例如:

[
    {points:7200,CommunityMemberId:1,firstname:'john',lastname:'blah' ....},
    ...
]

有人可以告诉我我做错了什么吗?

标签: linq

解决方案


将评论添加到末尾后编辑

我可以想象您在将 SQL 转换为 LINQ 时遇到问题。在尝试编写 LINQ 语句时,从您的需求开始通常比从 SQL 语句开始要容易得多。

在我看来,您与 CommunityMembers 有一张桌子。每个 CommunityMember 在属性 Id 中都有一个主键。

此外,每个 CommunityMember 都有零个或多个 CommunityMemberPointFeatures,即那些具有外键 CommunityMemberId 的 CommunityMemberPointFeatures,该外键等于它所属的 CommunityMember 的主键。

例如:CommunityMember [14] 的所有 CommunityMemberPointFeatures 的 CommunityMemberId 值都等于 14。

要求
如果我查看您的 SQL,在我看来,您想查询所有 CommunityMember,每个 CommunityMember 的所有 CommunityMemberPointFeatures 的属性 Point 的总和。

每当您要查询“带有零个或多个子项的项目”时,例如“学校及其学生”、“客户及其订单”、“社区成员及其 PointFeatures”,请考虑使用GroupJoin

GroupJoin 实际上是一个左外连接,后跟一个 GroupBy 以将左项与其所有右项组成组。

var result = dbContext.CommunityMembers               // GroupJoin CommunityMembers
    .GroupJoin(CommunityMemberPointFeatures,          // With CommunityMemberPointFeatures
    communityMember => communityMember.Id,            // from every CommunityMember take the Id
    pointFeature => pointFeature.CommunityMemberId,   // from every CommunityMemberPointFeature
                                                      // take the CommunityMemberId

// Parameter ResultSelector: take every CommunityMember, with all its matching 
// CommunityMemberPointFeatures to make one new object:
    (communityMember, pointFeaturesOfThisCommunityMember) => new
    {
        // Select the communityMember properties that you plan to use:
        Id = communityMember.Id,
        Name = communityMember.Name,
        ...

        // From the point features of this CommunityMember you only want the sum
        // or property Point:
        Points = pointFeaturesOfThisCommunityMember
                 .Select(pointFeature => pointFeature.Point)
                 .Sum(),

        // However, if you want more fields, you can use:
        PointFeatures = pointFeaturesOfThisCommunityMember.Select(pointFeature => new
        {
             Id = pointFeature.Id,
             Name = pointFeature.Name,
             ...

             // not needed, you know the value:
             // CommunityMemberId = pointFeature.CommunityMemberId,
        })
        .ToList(),
});

评论后编辑

如果需要,您可以省略选择您计划使用的值。

// Parameter ResultSelector:
(communityMember, pointFeaturesOfThisCommunityMember) => new
{
    CommunityMember = communityMember,
    PointFeatures = pointFeaturesOfThisCommunityMember.ToList(),
),

但是,我强烈建议不要这样做。如果 CommunityMember [14] 有一千个 PointFeature,那么每个 PointFeature 都会有一个值为 14 的外键。因此,您将传输该值 14 1001 次。多么浪费处理能力,更不用说您计划不使用的所有其他字段。

此外:如果你这样做,你就违反了信息隐藏:每当你的表在内部发生变化时,这个函数的结果就会改变。那是你要的吗?


推荐阅读