【问题标题】:How can I shorten this linq to sql query?如何缩短此 linq to sql 查询?
【发布时间】:2010-02-23 18:57:58
【问题描述】:

我正在制作一个日历,为了让自己更轻松,我打破了跨越数周的约会。

例如,1 月 1 日至 1 月 31 日跨越 6 周(我的日历总是 42 个单元格 - 6 x 7)。所以我基本上会在我的数据库中存储 6 行。

但是,我确实需要将所有这些行重新组合成一行。例如,如果我想以 Ical 格式导出我的日历。

我的数据库中有一个名为 bindingClassName 的字段,所有这些行都为该组任务获取相同的 unquie id,因此我可以轻松完成所有周。

// get all of the task rows by binding class name.
            var found = plannerDb.Calendars.Where(u => u.UserId == userId && u.BindingClassName == bindingClassName)
                .GroupBy(u => u.BindingClassName);



            List<Calendar> allAppoingments = new List<Calendar>();

            // go through each of the results and add it to a list of calendars
            foreach (var group in found)
            {

                foreach (var row in group)
                {

                    Calendar appointment = new Calendar();
                    appointment.AppointmentId = row.AppointmentId;
                    appointment.AllDay = row.AllDay;
                    appointment.BindingClassName = row.BindingClassName;
                    appointment.Description = row.Description;
                    appointment.EndDate = row.EndDate;
                    appointment.StartDate = row.StartDate;
                    appointment.Title = row.Title;
                    appointment.Where = row.Where;
                    appointment.UserId = row.UserId;

                    allAppoingments.Add(appointment);
                }
            }
            // order 
           var test = allAppoingments.OrderBy(u => u.StartDate);

           var firstAppointment = test.First();
           var LastAppointment = test.Last();

           Calendar newAppointment = new Calendar();
           newAppointment.UserId = firstAppointment.UserId;
           newAppointment.Description = firstAppointment.Description;
           newAppointment.AllDay = firstAppointment.AllDay;
           newAppointment.StartDate = firstAppointment.StartDate;
           newAppointment.Title = firstAppointment.Title;
           newAppointment.Where = firstAppointment.Where;
           newAppointment.BindingClassName = firstAppointment.BindingClassName;
           newAppointment.EndDate = LastAppointment.EndDate;

            return newAppointment;

所以基本上那个大块找到了所有具有相同绑定名称的约会。然后我遍历每一个并将其变成一个 Calendar 对象,最后一旦全部完成,我将获得第一条和最后一条记录以获取 startDate 和 endDate。

所以我不擅长 linq,但我不确定是否可以在 groupBy 之后添加一些东西来做我想做的事情。

编辑。

一旦我从用户那里获得所有约会,我就会尝试将所有约会组合在一起。

所以到目前为止我有这个

我试过这样的。

    var allApointments = calendar.GetAllAppointments(userId);
    var group = allApointments.GroupBy(u => u.BindingClassName).Select(u => new Calendar()).ToList

我希望它会自动填充每个组,但事实并非如此。所以我不确定是否不再需要 groupby。

编辑@管理员

您好,感谢您解释排序和分组。您是如何解释的,尽管看起来其中任何一个都可以。

就像您用于获取第一个和最后一个日期的代码一样,效果很好,并且可以满足我的要求。

我认为分组可能会起作用,因为最后虽然我只是希望有一行具有第一条记录的开始日期和最后一条记录的结束日期,但所有其他信息都是相同的。

所以我不知道是否会更难写,或者像我说的那样你的查询做我想要的。

但是,该查询是单独使用的。就像我仅在用户单击以在我的日历上查看该约会时使用该查询一样。通过单击约会,我可以获得有关该约会的所有信息,这就是我需要查看该任务是否跨越多天并确定约会何时开始以及何时结束的地方。

现在我需要另一个查询,我认为如果我可以将它们分组为我从您的解释中理解的方式会更好。我认为这是因为我想从该用户导出表中的所有记录。

因此,如果我通过绑定名称将它们排序到一个 continue 块中,我仍然需要一些循环来遍历所有记录并获取第一个和开始日期。因此,如果我可以一次性将其分组,最终结果将只是每组绑定名称的一个记录,并且它具有第一个开始日期和第一个和最后一个记录的最后一个结束日期会更好。

【问题讨论】:

    标签: c# linq linq-to-sql


    【解决方案1】:

    如果您实际上不使用该组,为什么还要对约会进行分组?看起来您只是单独使用它们。无论如何,您已经在 Where 子句中针对 BindingClassName 的单个值过滤了行,因此无论如何您最终只会得到 1 个(或 0 个)组。

    您可以将这一系列 foreach 循环重写为 SelectToList(),如下所示:

    var allAppointments = 
        plannerDb.Calendars.Where(
        row => row.UserId == userId && 
               row.BindingClassName == bindingClassName).OrderBy(
        row => row.StartDate).Select(
        row => new Calendar()
        {
            AppointmentId = row.AppointmentId,
            AllDay = row.AllDay,
            BindingClassName = row.BindingClassName,
            Description = row.Description,
            EndDate = row.EndDate,
            StartDate = row.StartDate,
            Title = row.Title,
            Where = row.Where,
            UserId = row.UserId
        }).ToList();
    

    这将按照您想要的顺序返回完整列表。但是,我很好奇您为什么要检索整个列表,而 看起来 您似乎只对第一次和最后一次约会感兴趣。你可以这样做:

    var baseQuery = 
            plannerDb.Calendars.Where(
            row => row.UserId == userId && 
                   row.BindingClassName == bindingClassName);
    
    var first = baseQuery.OrderBy(row => row.StartDate).First();
    var last = baseQuery.OrderByDescending(row => row.StartDate).Select(
               row => row.EndDate).First();
    
    return new Calendar()
    {
        AppointmentId = first.AppointmentId,
        AllDay = first.AllDay,
        BindingClassName = first.BindingClassName,
        Description = first.Description,
        EndDate = last,
        StartDate = first.StartDate,
        Title = first.Title,
        Where = first.Where,
        UserId = first.UserId
    });
    

    这应该会产生与您现在所拥有的相同的输出。但是,我会质疑这是否正是您想要的。假设您有两个约会:

    • 约会 1 开始于 1 月 5 日,结束于 1 月 10 日
    • 约会 2 开始于 1 月 6 日,结束于 1 月 7 日

    使用这个(和你的)逻辑,你会得到结束日期为 1 月 7 日,因为约会 2 的开始日期更大,但约会 1 实际上结束得更晚。我建议将第二个查询更改为:

    var last = baseQuery.OrderByDescending(row => row.EndDate).Select(
               row => row.EndDate).First();
    

    这会给你最大的结束日期,我认为这就是你真正想要的。

    编辑

    我认为您犯了(非常常见)将分组与排序混淆的错误。当您说要“按绑定名称对约会进行分组”时,听起来您想要一个完整的约会列表,并且您希望这些约会以这样一种方式排列,即所有具有特定绑定名称的约会形成一个连续的堵塞。如果是这种情况,您希望按绑定名称对列表进行排序,而不是对它们进行分组。分组获取整个列表并为每个分组子句生成一行,并允许您对剩余的列执行聚合函数。例如,假设我根据绑定名称对约会进行分组。这意味着我的结果集将包含每个绑定名称一行,然后我可以执行诸如查找最大开始或结束日期之类的事情;更正式地说,您可以指定聚合操作,这些操作采用一组数据(即开始日期列表)并返回单个数据(即最大开始日期)。

    除非我误会,听起来您仍想检索所有单独的分配,您只希望它们按绑定名称排列。如果是这种情况,只需OrderBy(row =&gt; row.BindingName) 就可以了。此外,您可能希望避免使用“组”一词,因为人们会认为您指的是我上面描述的那种分组。

    【讨论】:

    • 我相信 OP 使用最后一个约会来计算事件的结束日期:newAppointment.EndDate = LastAppointment.EndDate。
    • @anton:是的,我意识到了这一点。我已经进行了适当的修改。
    • 好吧,正如我在帖子中所说,我不擅长使用 linq,所以我试图从 sql 语句的角度来考虑它(我也不擅长)。然后试图将它们结合在一起,试图做出一些可行的陈述。是的,你是对的,我只是在寻找第一个和最后一个日期。我不认为你所说的实际上是一个问题。就像让我们以您的第一个示例约会 1 为例。它从 1 月 5 日到 10 日。我有其他代码可以将其分解为我的数据库中的 2 条记录。第一个是 1 月 5 日至 9 日,另一个是 1 月 10 日至 1 月 10 日。我会........
    • 然后从第一条记录(1 月 5 日到 9 日)中获取 id,获取其主键 id 字段(假设它是 1)并将其用作绑定类名称。所以在我的数据库中,两条记录都会有 bindName =“1”。那么你的第二次约会将是一个新记录,它将是 1 月 6 日到 1 月 7 日,并且会有一个 bindName = "2"。所以它不会被采用,因为它具有不同的绑定名称。现在,当他们导出日历时,它将查找相同的绑定名称并获取开始和结束日期,如果一切顺利,它应该是 1 月 5 日到 10 日。就像我说的那样,分手对我来说更容易,但其他的
    • 日历如果跨越数周,则可能不会破坏记录(他们可能会即时进行)。但是我只是没有时间在未来的版本中让它工作。
    【解决方案2】:

    顺便说一句,与 linq 无关,你看过 AutoMapper 吗?我目前正在使用它来填充来自 linq 的数据对象,我发现它对于摆脱只映射到 dtos 的大部分代码非常有用。它不会使代码的查询部分更短,但会减少:

    return new Calendar()
    {
        AppointmentId = first.AppointmentId,
        AllDay = first.AllDay,
        BindingClassName = first.BindingClassName,
        Description = first.Description,
        EndDate = last,
        StartDate = first.StartDate,
        Title = first.Title,
        Where = first.Where,
        UserId = first.UserId
    });
    

    到:

    return Mapper.Map(first,new Calendar{EndDate = last});
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多