@@ -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