【问题标题】:Using LINQ Expressions to Multiple Left Join and use ISNULL Functionality使用 LINQ 表达式进行多个左连接并使用 ISNULL 功能
【发布时间】:2018-07-11 01:25:30
【问题描述】:

我正在工作中构建步数跟踪网络应用。我正在使用最新的 EF Core。我正在与之交互的三个表:

  1. wg:WellnessGroup(WellnessGroupId,名称)
  2. wgu:WellnessGroupUser(查表:WellnessGroupId、EmployeeId)
  3. wsl: WellnessStepsLog (EmployeeId, StepCount)

我想要的是获得所有 WellnessGroups 和该组的总步数。如果该组还没有附加步骤,我希望 NULL 值为 0。我有这个 SQL 语句,它给了我所需的数据:

SELECT wg.Name, SUM(ISNULL(wsl.StepCount, 0)) AS steps
FROM dbo.WellnessGroup AS wg
LEFT JOIN dbo.WellnessGroupUser AS wgu
ON wgu.WellnessGroupId = wg.Id
LEFT JOIN dbo.WellnessStepsLog AS wsl
ON wsl.EmployeeId = wgu.AzureAdUserId
GROUP BY wg.Name
ORDER BY steps DESC;

我已经设法将 2 个 LINQ 表达式一起放在我的控制器上,这只会给我提供与它们相关联的步骤的 WellnessGroup,如果没有步骤,则不会给我 WellnessGroup 数据:

 var query = _dbContext.WellnessGroupUser
                    .Include(x => x.WellnessGroup)
                .Join(_dbContext.WellnessStepsLog, group => 
                group.AzureAdUserId, steps => steps.EmployeeId,
                    (group, steps) => new
                    {
                        Steps = steps.StepCount,
                        Date = steps.TrackedDate,
                        Group = group.WellnessGroup.Name

                    }).Where(x => x.Date >= yearToDate).Where(x => x.Date <= endDate);

 var stepsByGroup = query
                .GroupBy(x => x.Group)
                .Select(s => new
                {
                    Group = s.Key,
                    Date = s.Max(x => x.Date),
                    Steps = s.Sum(x => x.Steps)
                });

【问题讨论】:

    标签: c# asp.net entity-framework linq entity-framework-core


    【解决方案1】:

    一种方法是查询所有 WellnessGroup 并在其中构建总和作为第二个子查询。像这样:

    var query =
        db.WellnessGroup.Select(wg => new {
            wg.WellnessGroupId,
            sum = (int?) wg.WellnessGroupUser
                           .Sum(wgu => wgu.Employee.WellnessStepsLog.Sum(wsl => wsl.StepCount))
        });
    

    请注意,转换为 (int?) 很重要。否则 sum 被假定为 int,如果一行没有 sum,则会导致 InvalidOperationException。

    另一种方法是先建立所有的总和。然后与 WellnessGroups 进行外部连接:

    // sum up all stepcounts
    var q1 =
        from wgu in db.WellnessGroupUser
        from wsl in db.WellnessStepsLog
        where wgu.EmployeeId == wsl.EmployeeId
        group wsl.StepCount by wgu.WellnessGroupId
        into g
        select new {WellnessGroupId = g.Key, Sum = g.Sum()};
    
    // join with all WellnessGroups
    var q2 =
        from wg in db.WellnessGroup
        join s in q1 on wg.WellnessGroupId equals s.WellnessGroupId into sj
        from sum in sj.DefaultIfEmpty()
        select new {wg, sum = (int?) sum.Sum};
    

    编辑:

    由于 OP 后来在 cmets 中询问如何按多个字段进行分组。这是一个按WellnessGroup.WellnessGroupIdWellnessStepsLog.TrackedDate 月份分组的示例。通过将它们放在new { ... } 中,GroupBy 中可以有多个字段。因此,第一个查询为每个可能的 WellnessGroup / Month 组合创建一条线。第二个查询与之前一样执行 WellnessGroup 的外连接:

    var q1 =
        from wgu in db.WellnessGroupUser
        from wsl in db.WellnessStepsLog
        where wgu.EmployeeId == wsl.EmployeeId
        group wsl.StepCount by new { wgu.WellnessGroupId, wsl.TrackedDate.Month }
        into g
        select new {g.Key.WellnessGroupId, g.Key.Month, Sum = g.Sum()};
    
    // join with all WellnessGroups
    var q2 =
        from wg in db.WellnessGroup
        join s in q1 on wg.WellnessGroupId equals s.WellnessGroupId into sj
        from sum in sj.DefaultIfEmpty()
        select new {wg.WellnessGroupId, Month = (int?) sum.Month, Sum = (int?) sum.Sum};
    

    【讨论】:

    • 请注意,在 LINQ to SQL(我假设为 EF)中,到 int? 的转换是自动的,因为 SQL 值中可能存在 null
    • @NetMage 我也这么认为。但是在这个例子中它不起作用。我尝试过这个。如果仅删除“(int?)”,则会发生此异常:“System.InvalidOperationException: 'The cast to value type 'System.Int32' failed because the materialized value is null. 结果类型的通用参数或查询必须使用一个可以为空的类型。'"
    • @NetMage:看来你不能总是信任编译器:stackoverflow.com/questions/6864311/…
    • 啊 - 我没有使用Sum()
    • 成功了!感谢您提供直接的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多