I'm trying to implement a dynamic filter in a generic repository (.NET Core 3.1 + EF Core 3.1)
by building an Expression Tree, but the generated SQL query is never parameterized (I'm verifying the generated query via "Microsoft.EntityFrameworkCore.Database.Command": "Information" in appsettings.json and have EnableSensitiveDataLogging in Startup.cs)
The code to build an Expression Tree is the following (for sake of simplicity working with string values only here):
public static IQueryable<T> WhereEquals<T>(IQueryable<T> query, string propertyName, object propertyValue)
{
var pe = Expression.Parameter(typeof(T));
var property = Expression.PropertyOrField(pe, propertyName);
var value = Expression.Constant(propertyValue);
var predicateBody = Expression.Equal(
property,
value
);
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { typeof(T) },
query.Expression,
Expression.Lambda<Func<T, bool>>(predicateBody, new ParameterExpression[] { pe })
);
return query.Provider.CreateQuery<T>(whereCallExpression);
}
The approach works, but values are always incorporated inside a generated SQL query and I afraid that it could lead to SQL injections.
Here is an example of a generated query:
Microsoft.EntityFrameworkCore.Database.Command: Information: Executed DbCommand (33ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT [p].[Id], [p].[Name], [p].[FirstName], [p].[Created], [p].[CreatedBy], [p].[Updated], [p].[UpdatedBy]
FROM [Persons] AS [p]
WHERE [p].[Name] = N'smith'
Found a potential answer from a EF team member (@divega): Force Entity Framework to use SQL parameterization for better SQL proc cache reuse, managed it to work with Where method, but the generated SQL is still the same.
Tried to use System.Linq.Dynamic.Core, but it has the same issue (generated SQL query is not parameterized).
Is there a way to force Entity Framework Core to generate a parameterized query from an Expression Tree?
';TRUNC Table;that that last line of SQL becomesWHERE [p].[Name] = N''';DELETE FROM Table;'notWHERE [p].[Name] = N'';DELETE FROM Table;which seems safe enough, which is to say EF escapes the string properly. I'm probably missing something, so would be curious to know what you were thinking of.