使用LINQ to Entities生成不需要的请求

时间:2017-07-06 08:29:43

标签: c# sql performance linq linq-to-entities

为了优化方法,我查看代码的每一行以及每个生成的请求,以便最大限度地消除它们。我对以下部分表示严重怀疑:

计划包含多个项目。一个项目包含几个活动。我首先加载所有活动以满足未来需求。正如预期的那样,这会产生一个" SELECT WHERE IN"活动表上的请求

var planningProjectIds = p.PlanningProjects.Select(x => x.PlanningProjectId).ToList();
var planningActivities = _db.PlanningActivities.Where(x => planningProjectIds.Contains(x.PlanningProjectId));
var planningActivitiesArr = planningActivities.ToLookup(x => x.PlanningProjectId);

这是第一次尝试。以下代码生成不需要的请求(在日志行9b和10之间)

foreach (PlanningProject pp in p.PlanningProjects)
{
    // ...
    sw.WriteLine("9");
    pp.PlanningActivities = planningActivitiesArr[pp.PlanningProjectId].ToList();
    sw.WriteLine("9b");
    pp.PlanningActivities = pp.PlanningActivities.OrderBy(x => x.Position).ThenBy(x => x.PlanningActivityId).ToList();
    sw.WriteLine("10");
    // ...
}

这是第二次尝试。此代码不会生成查询

foreach (PlanningProject pp in p.PlanningProjects)
{
    // ...
    sw.WriteLine("9");
    pp.PlanningActivities = planningActivitiesArr[pp.PlanningProjectId]
        .OrderBy(x => x.Position)
        .ThenBy(x => x.PlanningActivityId)
        .ToList();
    sw.WriteLine("10");
    // ...
}
你能告诉我原因吗?提前谢谢你;)

以下是日志的摘录,我们可以看到额外的请求

9b
Opened connection at 06/07/2017 10:22:15 +02:00

SELECT 
    [Extent1].[PlanningActivityId] AS [PlanningActivityId], 
    [Extent1].[FriendlyName] AS [FriendlyName], 
    [Extent1].[PlanningProjectId] AS [PlanningProjectId], 
    [Extent1].[ActivityId] AS [ActivityId], 
    [Extent1].[ScheduleID] AS [ScheduleID], 
    [Extent1].[Position] AS [Position]
    FROM [dbo].[PlanningActivities] AS [Extent1]
    WHERE [Extent1].[PlanningProjectId] = @EntityKeyValue1


-- EntityKeyValue1: '847' (Type = Int32, IsNullable = false)

-- Executing at 06/07/2017 10:22:15 +02:00

-- Completed in 7 ms with result: SqlDataReader



Closed connection at 06/07/2017 10:22:15 +02:00

10

如果我忘记了有价值的细节来解决这个小问题,请不要犹豫告诉我。)

2 个答案:

答案 0 :(得分:1)

在您的第一个区块中,pp仍然连接到数据库上下文,因此lazily loadPlanningActivities。如果您在单个查询中执行所有操作并使用Include加载子实体,则可能会获得更好的性能和更少的查询。例如:

var projects = _db.Plannings
    .Where(pl => pl.PlanningId == 1234)
    .PlanningProjects
    .Include(pl => pl.PlanningProjects
        .Select(pp => pp.PlanningActivities))
    .ToList();

答案 1 :(得分:0)

当您枚举p.PlanningProjects时,您只将PlanningProject实体作为本地变量pp

此时不会填充任何导航属性。

foreach (PlanningProject pp in p.PlanningProjects)
{
    // ...
}

在调用属性getter以填充PlanningActivities之间的PlanningProject“9b”和“10”之间时,将查询数据库以进行这些计划活动:

sw.WriteLine("9b");
pp.PlanningActivities = pp.PlanningActivities.OrderBy(x => x.Position).ThenBy(x => x.PlanningActivityId).ToList();
sw.WriteLine("10");

因此查询:

SELECT ....
FROM [dbo].[PlanningActivities] where [PlanningProjectId] = @EntityKeyValue1

@EntityKeyValue1PlanningProject.PlanningProjectId变量的pp

如果您不想要提取查询,请使用include一次加载所有数据。