如何将值绑定到以编程方式生成的Linq表达式中的变量

时间:2013-10-08 23:06:45

标签: linq linq-to-entities c#-5.0

我正在尝试以编程方式生成表达式树(最终将在Linq-to-entity框架中使用)。

我可以通过一个例外使查询正常工作 - 它不会参数化查询 - 我希望Sql Server查询计划重用。

我读到为了生成的sql被参数化,表达式需要根据变量进行比较。但是,我无法弄清楚如何将值赋给表达式树中的变量。如果我只使用Expression.Constant它可以工作(但不是参数化)。

基本上是这样的:

public Expression<Func<T, bool>> FooEquals<T>(
    Expression<Func<T, int>> propertyExpression, int value)
{
    ParameterExpression param = propertyExpression.Parameters.Single();

    int targetValueForEqualityComparison = 9;

    //There's some "special sauce" here which is why I don't 
    //use propertyExpression directly
    var body = Expression.Property(param, GetPropertyName(propertyExpression)); 

    //If I just use Expression.Constant, it works, but doesn't parametrize.
    //var equalExpression = ParameterExpression.Equal(body,
    //   Expression.Constant(targetValueForEqualityComparison, typeof(int)));

    var variable = Expression
                   .Variable(typeof(int), "targetValueForEqualityComparison");

    var assigned = Expression.Assign(variable, 
            Expression.Constant(targetValueForEqualityComparison, typeof(int)));

    //throws InvalidOperaitonException: "The parameter was not bound in the 
    //specified Linq to Entities query expression
    var equalExpression = ParameterExpression.Equal(body, variable); 

    //throws NotSupportedException: "Unknown LINQ expression of type 'Assign'.
    var equalExpression = ParameterExpression.Equal(body, assigned); 

    return Expression.Lambda<Func<T, bool>>(equalExpression, param);
}

如何将值正确绑定到变量表达式,以便Linq-to-EntityFramework将参数化查询?

1 个答案:

答案 0 :(得分:1)

我继续尝试,因为我很好奇。以下似乎导致相同的SQL,至少在与Linq-to-SQL一起使用时(LINQPad比EF更容易)。我想它应该和EF一样工作。

看起来像是一个非常复杂的方式来传递一个整数,但因为这是编译器为普通lambda函数生成的,我想这就是SQL生成器所寻找的。

// Given this class, which is equivalent to the compiler-generated class
class Holder {
    public int AnInteger;
}

int id = 1;

// You get the same SQL with a plain lambda function

var query = db.Items.Where(i => i.Id == id);

// or with a handwritten expression:

var arg = Expression.Parameter(typeof(Item), "i");

var paramHolder = new Holder { AnInteger = id };

// essentially, (i) => i.Id == paramHolder.AnInteger
var lambda = Expression.Lambda<Func<Item, bool>>(
    Expression.Equal(
        Expression.Property(arg, "Id"),
        Expression.Field(
            Expression.Constant(paramHolder), "AnInteger")),
    arg);

// the SQL this translates to is equivalent to that of the first query
var query2 = db.Items.Where(lambda);