【发布时间】:2016-01-17 23:32:26
【问题描述】:
在阅读Entity Framework performance 上的一篇文章时,我偶然发现了这条信息:
其次,问题[SQL Server 不会重用执行计划]首先出现,因为(由于实现细节)将 int 传递给 Skip() 和 Take( ) 方法,Entity Framework 无法查看它们是否传递了像 Take(100) 这样的绝对值,或者像 Take(resultsPerPage) 这样的变量,因此它不知道该值是否应该被参数化。
建议的解决方案是改变这种代码风格:
var schools = db.Schools
.OrderBy(s => s.PostalZipCode)
.Skip(model.Page * model.ResultsPerPage)
.Take(model.ResultsPerPage)
.ToList();
进入这种风格:
int resultsToSkip = model.Page * model.ResultsPerPage;
var schools = db.Schools
.OrderBy(s => s.PostalZipCode)
.Skip(() => resultsToSkip) //must pre-calculate this value
.Take(() => model.ResultsPerPage)
.ToList();
这让 Entity Framework 知道这些是变量并且生成的 SQL 应该被参数化,这反过来又允许执行计划被重用。
我们的应用程序中有一些代码以相同的方式使用变量,但我们必须在运行时构建表达式,因为事先不知道类型。
这是它过去的样子:
var convertedId = typeof(T).GetConvertedIdValue(id);
var prop = GetIdProperty(typeof(T));
var itemParameter = Expression.Parameter(typeof(T), "item");
var whereExpression = Expression.Lambda<Func<T, bool>>
(
Expression.Equal(
Expression.Property(
itemParameter,
prop.Name
),
Expression.Constant(convertedId)
),
new[] { itemParameter }
);
return Get<T>().Where(whereExpression);
问题是使用Expression.Constant(convertedId) 会导致将常量插入到生成的SQL 中。这会导致您查找的每个新项目的 SQL 都发生变化,从而停止任何执行计划缓存:
WHERE [Extent1].[Id] = 1234
和:
WHERE [Extent1].[Id] = 1235
和:
WHERE [Extent1].[Id] = 1236
那么问题是如何使用表达式构建来强制对生成的 SQL 进行参数化?() => convertedId 语法将不起作用。我已经在下面回答了。
【问题讨论】:
-
我不明白,这里的问题是什么?
-
问题是在使用 Expression.Constant 时如何转换上面的代码以生成参数化 SQL,因为
() => convertedId语法不起作用。 -
我已更新主帖以明确包含该问题。
标签: c# sql-server entity-framework linq