Asp.net 如何编写LINQ查询,将子表过滤到特定时间段,并对子表的结果求和?

Asp.net 如何编写LINQ查询,将子表过滤到特定时间段,并对子表的结果求和?,asp.net,linq,linq-to-sql,subquery,left-join,Asp.net,Linq,Linq To Sql,Subquery,Left Join,如果我必须执行以下操作,那么这是在LINQ中执行左外连接的最有效方法 按开始日期和结束日期筛选表2 表1中的所有行必须保留,即使表2的筛选没有返回任何行 必须对结果进行分组,以便对表2中的列求和 例如(示例代码变量名因适当的原因更改),假设我有一个包含两个表的数据库。表1列出了带有建筑代码、门ID和当前状态(打开或关闭)的门-建筑代码和门ID是主键。表2列出了所有门的事件(一个事件是打开或关闭)以及时间戳。因此,这些列是建筑代码、门ID、时间戳、打开和关闭。打开和关闭是相应事件列中带有1的整

如果我必须执行以下操作,那么这是在LINQ中执行左外连接的最有效方法

  • 按开始日期和结束日期筛选表2
  • 表1中的所有行必须保留,即使表2的筛选没有返回任何行
  • 必须对结果进行分组,以便对表2中的列求和
例如(示例代码变量名因适当的原因更改),假设我有一个包含两个表的数据库。表1列出了带有建筑代码、门ID和当前状态(打开或关闭)的门-建筑代码和门ID是主键。表2列出了所有门的事件(一个事件是打开或关闭)以及时间戳。因此,这些列是建筑代码、门ID、时间戳、打开和关闭。打开和关闭是相应事件列中带有1的整数。建筑代码和门ID上的两个表之间存在外键关系

对于我的查询,我需要返回具有当前门状态的所有唯一门的列表,以及选定时间段内所有打开和关闭事件的总和。即使在所选时间段内未发生任何事件,也必须为每个门返回条目

下面是我能想到的最好的LINQ代码。这是可行的,但似乎效率很低,也很难理解。您将如何使其更高效、更容易理解

var query = 
    from doors in Context.Doors
    join fevents in
        (
        from events in db.Events
        where events.TimeStamp >= date1 && events.TimeStamp <= date2
        select new { events.BuildingCode, events.DoorID, events.TimeStamp, events.Opening, events.Closing }
        )
    on new { doors.BuildingCode, doors.DoorID } equals { fevents.BuildingCode, fevents.DoorID }
    into g1
    from c in g1.DefaultIfEmpty()
    group c by new
    {
        doors.BuildingCode,
        doors.DoorID,
        doors.DoorStatus
    } into g2
    select new
    {
        BuildingCode = g2.Key.BuildingCode,
        DoorID = g2.Key.DoorID,
        Status = g2.Key.DoorStatus
        NumOpenings = g2.Sum(i => (i == null ? 0 : i.Opening)),
        NumClosings = g2.Sum(i => (i == null ? 0 : i.Closing))
    };
var查询=
从上下文中的doors。doors
加入
(
来自db.events中的事件
其中events.TimeStamp>=date1&&events.TimeStamp(i==null?0:i.Opening)),
NumClosings=g2.Sum(i=>(i==null?0:i.Closing))
};

我认为这稍微容易一点

var query = 
    from doors in Context.Doors
    from c in db.Events
                .Where(events => doors.BuildingCode == events.BuildingCode)
                .Where(events => doors.DoorID == events.DoorID)
                .Where(events => events.TimeStamp >= date1 && events.TimeStamp <= date2)
                .Select(events => new { events.BuildingCode, events.DoorID, events.TimeStamp, events.Opening, events.Closing })
                .DefaultIfEmpty()
    group c by new
    {
        doors.BuildingCode,
        doors.DoorID,
        doors.DoorStatus
    } into g2
    select new
    {
        BuildingCode = g2.Key.BuildingCode,
        DoorID = g2.Key.DoorID,
        Status = g2.Key.DoorStatus
        NumOpenings = g2.Sum(i => (i == null ? 0 : i.Opening)),
        NumClosings = g2.Sum(i => (i == null ? 0 : i.Closing))
    };
var查询=
从上下文中的doors。doors
从c到db.Events
.Where(events=>doors.BuildingCode==events.BuildingCode)
.Where(events=>doors.DoorID==events.DoorID)
.Where(events=>events.TimeStamp>=date1&&events.TimeStamp new{events.BuildingCode,events.DoorID,events.TimeStamp,events.Opening,events.Closing})
.DefaultIfEmpty()
c组由纽约
{
doors.BuildingCode,
门,门牌,
门,门的状态
}进入g2
选择新的
{
BuildingCode=g2.Key.BuildingCode,
DoorID=g2.Key.DoorID,
状态=g2.Key.door状态
NumOpenings=g2.Sum(i=>(i==null?0:i.Opening)),
NumClosings=g2.Sum(i=>(i==null?0:i.Closing))
};

来自@Encalucci的答案帮助我想出了一个稍微不同的解决方案,我认为它更具可读性,尽管效率可能更低

var query = 
    from doors in Context.Doors
    from events in doors.Events
                        .Where(i => i.TimeStamp >= date1 && i.TimeStamp <= date2)
                        .DefaultIfEmpty()
    group new { doors, events }
    by doors into g
    select new 
    { 
        BuildingCode = g.Key.BuildingCode,
        DoorID = g.Key.DoorID,
        Status = g.Key.DoorStatus,
        NumOpenings = g.Sum(i => (i.events == null ? 0 : i.events.Opening)),
        NumClosings = g.Sum(i => (i.events == null ? 0 : i.events.Closing))
    };
var查询=
从上下文中的doors。doors
来自门中的事件。事件
。其中(i=>i.TimeStamp>=date1&&i.TimeStamp(i.events==null?0:i.events.Opening)),
NumClosings=g.Sum(i=>(i.events==null?0:i.events.Closing))
};
请注意,按日期筛选的替代方法将直接在求和函数中,如下所示,但效率要低得多,因为将从数据库检索所有记录,然后在本地进行筛选

...
//from events in doors.Events
//                    .Where(i => i.TimeStamp >= date1 && i.TimeStamp <= date2)
//                    .DefaultIfEmpty()
from events in doors.Events
                    .DefaultIfEmpty()
...
NumOpenings = g.Sum(i => (i.events == null ? 0 : (i.events.Timestamp >= date1 && i.events.TimeStamp <= date2) ? i.events.Opening : 0)),
NumClosings = g.Sum(i => (i.events == null ? 0 : (i.events.Timestamp >= date1 && i.events.TimeStamp <= date2) ? i.events.Closing : 0))
...
。。。
//来自门中的事件。事件

//。其中(i=>i.TimeStamp>=date1&&i.TimeStamp(i.events==null?0:(i.events.TimeStamp>=date1&&i.events.TimeStamp)(i.events==null?0:(i.events.Timestamp>=date1&&i.events.Timestamp感谢您的回答@aducci。非常有用。它让我以不同的方式思考了这个问题。我发布了稍微不同的答案,作为其他人的参考。