Description
In F#, closure parameters are integrated into the query expression tree as constant nodes - see dotnet/fsharp#12841 for the full details. This means that by default, all EF queries generate SQL with constants rather than parameters.
A workaround is to use a F# ref, which gets properly parameterized by EF Core:
let i = ref 8
db.Events.Where(fun x -> x.Data = i.Value).ToList();
However, while this works well with local variables, it's problematic for use with function parameters, since the parameter needs to be typed as Ref<T>
(it isn't great to require people to change method signatures just so that an EF query inside get properly parameterized). Note that the variable name is also lost, so the SQL contains @__Value_0
.
We could introduced an EF.Param() mechanism, which is identified by EF Core and forces parameterization. The same mechanism could be used in C# to force parameterization for better perf when two queries use different constants, without needing to extract a variable:
var query1 = await ctx.Blogs.Where(b => b.Name == EF.Param("foo")).ToListAsync();
...
var query2 = await ctx.Blogs.Where(b => b.Name == EF.Param("bar")).ToListAsync();
EF.Param could optionally also accept the SQL parameter name, to produce more readable SQL queries.
/cc @dsyme @NinoFloris