3

我正在尝试以编程方式生成一个表达式树(最终将在 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 将对查询进行参数化?

4

1 回答 1

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);
于 2013-10-08T23:33:06.487 回答