我正在尝试以编程方式生成表达式树(最终将在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将参数化查询?
答案 0 :(得分:1)
我继续尝试,因为我很好奇。以下似乎导致相同的SQL,至少在与Linq-to-SQL一起使用时(LINQPad比EF更容易)。我想它应该和EF一样工作。
看起来像是一个非常复杂的方式来传递一个整数,但因为这是编译器为普通lambda函数生成的,我想这就是SQL生成器所寻找的。 p>
// 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);