While reading an article on Entity Framework performance, I came across this information:
Secondly, the problem [SQL Server will not reuse the execution plan] occurs firstly, because (due to implementation details), when passing an int to the Skip () and Take () methods, the Entity Framework cannot determine if they were absolute values, such as Take (100), or a variable of type Take (resultsPerPage) are passed, so it does not know whether to value the parameter.
The proposed solution is to change this code style:
var schools = db.Schools .OrderBy(s => s.PostalZipCode) .Skip(model.Page * model.ResultsPerPage) .Take(model.ResultsPerPage) .ToList();
In this style:
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();
This allows the Entity Framework to know that these are variables, and that the generated SQL must be parameterized, which in turn allows reuse of the execution plan.
We have some code in our application that uses variables in the same way, but we need to build an expression at runtime because the type is not known in advance.
Here's how it looked:
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);
The problem is that using Expression.Constant(convertedId) causes the constant to be inserted into the generated SQL. This forces SQL to change every new item you are viewing, which stops the execution plan caching:
WHERE [Extent1].[Id] = 1234
and
WHERE [Extent1].[Id] = 1235
and
WHERE [Extent1].[Id] = 1236
The question then is, How can you use the creation of an expression in such a way as to force parameterization of the generated SQL? Syntax () => convertedId will not work. I answered this below.