使用 LINQ to Entities 生成不需要的请求
Unwanted request generated with LINQ to Entities
为了优化一个方法,我会查看我的每一行代码以及每个生成的请求,以便最大限度地消除它们。我对以下部分有严重的怀疑:
一个规划包含多个项目。一个项目包含多个活动。我首先加载所有活动以备将来需要。正如预期的那样,这会在活动 table
上生成 "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
如果我能忘记解决这个小问题的宝贵细节,请不要犹豫告诉我:)
在您的第一个块中,pp
仍连接到数据库上下文,因此 lazily load the PlanningActivities
. You may get better performance and fewer queries if you do everything in a single query and use Include
将加载子实体。例如:
var projects = _db.Plannings
.Where(pl => pl.PlanningId == 1234)
.PlanningProjects
.Include(pl => pl.PlanningProjects
.Select(pp => pp.PlanningActivities))
.ToList();
当您枚举 p.PlanningProjects
时,您只会将 PlanningProject
实体作为局部变量 pp
.
此时不填充任何导航属性。
foreach (PlanningProject pp in p.PlanningProjects)
{
// ...
}
一旦您调用 属性 getter 为“9b”和“10”之间的 PlanningProject
填充 PlanningActivities
,数据库将查询这些计划活动:
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
@EntityKeyValue1
是 pp
变量的 PlanningProject.PlanningProjectId
。
如果您不需要提取查询,请使用 include 一次加载所有数据。
为了优化一个方法,我会查看我的每一行代码以及每个生成的请求,以便最大限度地消除它们。我对以下部分有严重的怀疑:
一个规划包含多个项目。一个项目包含多个活动。我首先加载所有活动以备将来需要。正如预期的那样,这会在活动 table
上生成 "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
如果我能忘记解决这个小问题的宝贵细节,请不要犹豫告诉我:)
在您的第一个块中,pp
仍连接到数据库上下文,因此 lazily load the PlanningActivities
. You may get better performance and fewer queries if you do everything in a single query and use Include
将加载子实体。例如:
var projects = _db.Plannings
.Where(pl => pl.PlanningId == 1234)
.PlanningProjects
.Include(pl => pl.PlanningProjects
.Select(pp => pp.PlanningActivities))
.ToList();
当您枚举 p.PlanningProjects
时,您只会将 PlanningProject
实体作为局部变量 pp
.
此时不填充任何导航属性。
foreach (PlanningProject pp in p.PlanningProjects)
{
// ...
}
一旦您调用 属性 getter 为“9b”和“10”之间的 PlanningProject
填充 PlanningActivities
,数据库将查询这些计划活动:
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
@EntityKeyValue1
是 pp
变量的 PlanningProject.PlanningProjectId
。
如果您不需要提取查询,请使用 include 一次加载所有数据。