gpt4 book ai didi

c# - Entity Framework Core 中表达式树的参数化查询

转载 作者:行者123 更新时间:2023-12-05 02:08:49 27 4
gpt4 key购买 nike

我正在尝试在通用存储库(.NET Core 3.1 + EF Core 3.1)中实现动态过滤器通过构建表达式树,但生成的 SQL 查询从未被参数化(我正在通过 appsettings.json 中的 "Microsoft.EntityFrameworkCore.Database.Command": "Information" 验证生成的查询并在 Startup.cs 中有 EnableSensitiveDataLogging)

构建表达式树的代码如下(为了简单起见,此处仅使用字符串值):

    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);
}

该方法可行,但值总是包含在生成的 SQL 查询中,我担心它可能会导致 SQL 注入(inject)。

这是一个生成的查询示例:

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'

从 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 参数,因此如果您创建变量引用(在 C# 中是总是一个字段引用),那么你将得到一个参数化查询。最简单的解决方案似乎是复制编译器处理 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);
}
}

如果你需要传入一个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);
}

关于c# - Entity Framework Core 中表达式树的参数化查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60515202/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com