Skip to content

Commit ec5f8d5

Browse files
author
Bart Koelman
authored
Fixed inefficient cache usage. (#694)
* Fixed inefficient cache usage. By turning constant expressions (whose value comes from query string) into parameters, the parsed query can be reused for varying query string parameters. * Empty commit to restart timed-out Travis build
1 parent 668f071 commit ec5f8d5

File tree

1 file changed

+28
-3
lines changed

1 file changed

+28
-3
lines changed

src/JsonApiDotNetCore/Extensions/IQueryableExtensions.cs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ private static IQueryable<TSource> CallGenericWhereMethod<TSource>(IQueryable<TS
247247
PropertyInfo relationProperty = null;
248248
PropertyInfo property = null;
249249
MemberExpression left;
250-
ConstantExpression right;
250+
Expression right;
251251

252252
// {model}
253253
var parameter = Expression.Parameter(concreteType, "model");
@@ -287,8 +287,8 @@ private static IQueryable<TSource> CallGenericWhereMethod<TSource>(IQueryable<TS
287287
// convert the incoming value to the target value type
288288
// "1" -> 1
289289
var convertedValue = TypeHelper.ConvertType(filter.Value, property.PropertyType);
290-
// {1}
291-
right = Expression.Constant(convertedValue, property.PropertyType);
290+
291+
right = CreateTupleAccessForConstantExpression(convertedValue, property.PropertyType);
292292
}
293293

294294
var body = GetFilterExpressionLambda(left, right, filter.Operation);
@@ -302,6 +302,31 @@ private static IQueryable<TSource> CallGenericWhereMethod<TSource>(IQueryable<TS
302302
}
303303
}
304304

305+
private static Expression CreateTupleAccessForConstantExpression(object value, Type type)
306+
{
307+
// To enable efficient query plan caching, inline constants (that vary per request) should be converted into query parameters.
308+
// https://stackoverflow.com/questions/54075758/building-a-parameterized-entityframework-core-expression
309+
310+
// This method can be used to change a query like:
311+
// SELECT ... FROM ... WHERE x."Age" = 3
312+
// into:
313+
// SELECT ... FROM ... WHERE x."Age" = @p0
314+
315+
// The code below builds the next expression for a type T that is unknown at compile time:
316+
// Expression.Property(Expression.Constant(Tuple.Create<T>(value)), "Item1")
317+
// Which represents the next C# code:
318+
// Tuple.Create<T>(value).Item1;
319+
320+
MethodInfo tupleCreateMethod = typeof(Tuple).GetMethods()
321+
.Single(m => m.Name == "Create" && m.IsGenericMethod && m.GetGenericArguments().Length == 1);
322+
MethodInfo constructedTupleCreateMethod = tupleCreateMethod.MakeGenericMethod(type);
323+
324+
ConstantExpression constantExpression = Expression.Constant(value, type);
325+
326+
MethodCallExpression tupleCreateCall = Expression.Call(constructedTupleCreateMethod, constantExpression);
327+
return Expression.Property(tupleCreateCall, "Item1");
328+
}
329+
305330
private static IQueryable<TSource> CallGenericSelectMethod<TSource>(IQueryable<TSource> source, List<string> columns)
306331
{
307332
var sourceBindings = new List<MemberAssignment>();

0 commit comments

Comments
 (0)