构建参数化的EntityFramework核心表达式

mix*_*tch 4 c# asp.net entity-framework-core

嗨,我正在尝试构建一个表达式,以通过其主键获取通用实体,并获取参数化的sql查询。

当前,我可以获取正确的WHERE查询,但未对其进行参数化。

public async Task<TDbo> Get(TKey key, Expression<Func<TEntity, TKey>> keySelector)
{
    var propertyRef = keySelector.Body;
    var parameter = keySelector.Parameters[0];
    var constantRef = Expression.Constant(key);
    var equals = Expression.Equal(propertyRef, constantRef);
    var comparer = Expression.Lambda<Func<TEntity, bool>>(equals, parameter);

    return await _context.Set<TDbo>().SingleOrDefaultAsync(comparer);
}
Run Code Online (Sandbox Code Playgroud)

这将导致以下查询: SELECT e.\"Id\", e.\"Name\" \r\n FROM \"People\" AS e\r\nWHERE e.\"Id\" = 1\r\nLIMIT 2,而不是通缉: SELECT e.\"Id\", e.\"Name\" \r\n FROM \"People\" AS e\r\nWHERE e.\"Id\" = @__s_0\r\nLIMIT 2

Iva*_*oev 5

是因为Expression.Constant(key)。值常量表达式未由查询转换器参数化。您需要一个引用另一个表达式的属性或字段的表达式(可以是常量)。基本上,这就是C#编译器发出的用于闭包的内容。

一种方法是实际使用C#编译器创建带闭包的lambda表达式并获取主体:

Expression<Func<TKey>> keyValue = () => key;
var variableRef = key.Body;
Run Code Online (Sandbox Code Playgroud)

(这variableRef是您的替代品constantRef

另一种方法是使用匿名,元组或特定的类类型创建显式的闭包实例并绑定相应的属性或字段。例如,使用匿名类型:

var variableRef = Expression.Property(Expression.Constant(new { key }), "key");
Run Code Online (Sandbox Code Playgroud)

或搭配System.Tuple

var variableRef = Expression.Property(Expression.Constant(Tuple.Create(key)), "Item1");
Run Code Online (Sandbox Code Playgroud)

实际的方法并不重要(我个人更喜欢使用lambda的第一个变体)-所有这些都会导致EF Core查询转换器创建参数。