Vit*_*aly 5 c# linq-to-sql entity-framework-core .net-core
我试图通过构建表达式树在通用存储库(.NET Core 3.1 + EF Core 3.1)中实现动态过滤器,但生成的 SQL 查询从未参数化(我正在通过"Microsoft.EntityFrameworkCore.Database.Command": "Information"
appsettings.json验证生成的查询)并EnableSensitiveDataLogging
在 Startup.cs 中)
构建表达式树的代码如下(为了简单起见,仅在此处使用字符串值):
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);
}
Run Code Online (Sandbox Code Playgroud)
该方法有效,但值总是合并到生成的 SQL 查询中,我担心它可能会导致 SQL 注入。
以下是生成的查询的示例:
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'
Run Code Online (Sandbox Code Playgroud)
从 EF 团队成员 (@divega) 那里找到了一个潜在的答案: Force Entity Framework to use SQL parameterization for better SQL proc cache reuse,管理它与Where方法一起使用,但生成的SQL仍然是相同的。
尝试使用System.Linq.Dynamic.Core,但它有同样的问题(生成的SQL查询未参数化)。
有没有办法强制 Entity Framework Core 从表达式树生成参数化查询?
您提供的链接解释了 EF 对变量值使用 SQL 参数,因此如果Expression.Constant
您创建变量引用(在 C# 中始终是字段引用),则无需为传入的值创建 SQL 参数,您将获得参数化查询。最简单的解决方案似乎是复制编译器处理 lambda 外部作用域变量引用的方式,即创建一个类对象来保存该值,并引用该值。
与 不同的是Expression.Constant
,获取参数的实际类型并不容易object
,因此将其更改为泛型类型:
public static class IQueryableExt {
private sealed class holdPropertyValue<T> {
public T v;
}
public static IQueryable<T> WhereEquals<T, TValue>(this IQueryable<T> query, string propertyName, TValue propertyValue) {
// p
var pe = Expression.Parameter(typeof(T), "p");
// p.{propertyName}
var property = Expression.PropertyOrField(pe, propertyName);
var holdpv = new holdPropertyValue<TValue> { v = propertyValue };
// holdpv.v
var value = Expression.PropertyOrField(Expression.Constant(holdpv), "v");
// p.{propertyName} == holdpv.v
var whereBody = Expression.Equal(property, value);
// p => p.{propertyName} == holdpv.v
var whereLambda = Expression.Lambda<Func<T, bool>>(whereBody, pe);
// Queryable.Where(query, p => p.{propertyName} == holdpv.v)
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { typeof(T) },
query.Expression,
whereLambda
);
// query.Where(p => p.{propertyName} == holdpv.v)
return query.Provider.CreateQuery<T>(whereCallExpression);
}
}
Run Code Online (Sandbox Code Playgroud)
如果您需要传入 an object
,则添加到正确类型的转换(这不会影响生成的 SQL)会更简单,而不是动态创建正确的类型holdPropertyValue
并为其分配值,因此:
public static IQueryable<T> WhereEquals2<T>(this IQueryable<T> query, string propertyName, object propertyValue) {
// p
var pe = Expression.Parameter(typeof(T), "p");
// p.{propertyName}
var property = Expression.PropertyOrField(pe, propertyName);
var holdpv = new holdPropertyValue<object> { v = propertyValue };
// Convert.ChangeType(holdpv.v, p.{propertyName}.GetType())
var value = Expression.Convert(Expression.PropertyOrField(Expression.Constant(holdpv), "v"), property.Type);
// p.{propertyName} == Convert.ChangeType(holdpv.v, p.{propertyName}.GetType())
var whereBody = Expression.Equal(property, value);
// p => p.{propertyName} == Convert.ChangeType(holdpv.v, p.{propertyName}.GetType())
var whereLambda = Expression.Lambda<Func<T, bool>>(whereBody, pe);
// Queryable.Where(query, p => p.{propertyName} == Convert.ChangeType(holdpv.v, p.{propertyName}.GetType()))
var whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new[] { typeof(T) },
query.Expression,
whereLambda
);
// query.Where(query, p => p.{propertyName} == Convert.ChangeType(holdpv.v, p.{propertyName}.GetType()))
return query.Provider.CreateQuery<T>(whereCallExpression);
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
5902 次 |
最近记录: |