PAYATHON 2026

如何在 EF6 的 LINQ 查询中使用 Sum 聚合函数?

支付小周

结论

计算某个用户全部付款记录的总额时,不需要使用 group by。筛选出该用户的记录后,直接调用 Sum 即可:

decimal totalPayments = portalEntities.tblPortalPayments
    .Where(p => p.userId == _userID)
    .Sum(p => p.paymentsToDate);

对应的 SQL 查询是:

SELECT SUM(paymentsToDate)
FROM tblPortalPayments
WHERE userId = @userId

为什么不能使用 c.Sum

原查询中的 c 是一条 tblPortalPayment 实体记录,只能访问这条记录的字段或属性,例如 paymentDate 和 paymentsToDate。

Sum 不是实体的属性,而是用于集合或分组的 LINQ 聚合方法。因此,不能对单条记录调用 c.Sum(...),应该对 Where 返回的记录集合调用:

portalEntities.tblPortalPayments
    .Where(p => p.userId == _userID)
    .Sum(p => p.paymentsToDate);

EF6 会将这段 LINQ 转换成数据库端的 SUM 查询,而不是先把所有付款记录加载到内存中再计算。

处理没有匹配记录的情况

没有匹配记录时,SQL 的 SUM 可能返回 NULL。为了避免查询结果为空时发生异常,可以先把参与计算的字段转换为可空类型,再设置默认值:

decimal totalPayments = portalEntities.tblPortalPayments
    .Where(p => p.userId == _userID)
    .Sum(p => (decimal?)p.paymentsToDate) ?? 0m;

如果 paymentsToDate 的实际类型不是 decimal,需要改用对应的可空类型。例如,该字段为 double 时:

double totalPayments = portalEntities.tblPortalPayments
    .Where(p => p.userId == _userID)
    .Sum(p => (double?)p.paymentsToDate) ?? 0d;

也可以写成查询表达式:

decimal totalPayments =
    (from p in portalEntities.tblPortalPayments
     where p.userId == _userID
     select (decimal?)p.paymentsToDate)
    .Sum() ?? 0m;

按日期分别求和时才需要分组

如果要计算每个付款日期的合计金额,才需要使用 group by:

var paymentsByDate =
    from p in portalEntities.tblPortalPayments
    where p.userId == _userID
    group p by p.paymentDate into paymentGroup
    select new
    {
        PaymentDate = paymentGroup.Key,
        Sum = paymentGroup.Sum(p => p.paymentsToDate)
    };

这里的 paymentGroup 是一组记录,因此可以调用 paymentGroup.Sum(...)。查询会返回多条结果,每个日期对应一个合计金额,这与计算全部记录的总额不同。

关于 portalEntities 属性

这个属性不影响 Sum 的用法,但当前实现有两个问题:

  • catch 块吞掉了异常。一旦数据库上下文创建失败,就无法得知真正的原因。
  • DbContext 持有数据库连接等资源,需要明确管理其生命周期并及时释放。

如果操作范围较小,可以使用 using:

decimal totalPayments;

using (var context = new portalEntities())
{
    totalPayments = context.tblPortalPayments
        .Where(p => p.userId == _userID)
        .Sum(p => (decimal?)p.paymentsToDate) ?? 0m;
}

如果上下文由依赖注入或应用框架统一管理,应遵循框架规定的生命周期。无论采用哪种方式,都不应静默忽略创建 portalEntities 时发生的异常。

备注:内容仅供参考。