我试图弄清楚是否有办法强制C#表达式将部分表达式转换为值。我正在调用一个接受定义查询的表达式的方法。我在一个标识查询的对象中有一系列值。这是一个简化的例子:
var identifier = new { id = 5 };
context.SomeMethod(i=>i.Id == identifier.Id);
这失败了。基于错误,我看到表达式试图合并" identifier.Id"进入表达而不是解析" identifier.Id"它的值为5,以下代码有效:
var id = 5;
context.SomeMethod(i=>i.Id == id)
虽然这有效,但这是一个简化的例子,在我的实际代码中,这将是痛苦的。所以我的问题是,是否有一些语法可以用来包装表达式的一部分来强制它解析为一个值?
答案 0 :(得分:1)
在this blog entry中,它讨论了如何简化Expression
。它采用两遍方法,首先将所有不具有参数且没有参数的节点标记为子节点,然后再进行另一次传递,评估这些节点,以便任何可以在不依赖参数 评估的情况下计算。
这是一些带有一些小调整的代码:
public static class Evaluator
{
/// <summary>
/// Performs evaluation & replacement of independent sub-trees
/// </summary>
/// <param name="expression">The root of the expression tree.</param>
/// <param name="fnCanBeEvaluated">A function that decides whether a given expression node can be part of the local function.</param>
/// <returns>A new tree with sub-trees evaluated and replaced.</returns>
public static Expression PartialEval(Expression expression, Func<Expression, bool> fnCanBeEvaluated)
{
return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression);
}
/// <summary>
/// Performs evaluation & replacement of independent sub-trees
/// </summary>
/// <param name="expression">The root of the expression tree.</param>
/// <returns>A new tree with sub-trees evaluated and replaced.</returns>
public static Expression PartialEval(Expression expression)
{
return PartialEval(expression, Evaluator.CanBeEvaluatedLocally);
}
private static bool CanBeEvaluatedLocally(Expression expression)
{
return expression.NodeType != ExpressionType.Parameter;
}
/// <summary>
/// Evaluates & replaces sub-trees when first candidate is reached (top-down)
/// </summary>
class SubtreeEvaluator : ExpressionVisitor
{
HashSet<Expression> candidates;
internal SubtreeEvaluator(HashSet<Expression> candidates)
{
this.candidates = candidates;
}
internal Expression Eval(Expression exp)
{
return this.Visit(exp);
}
public override Expression Visit(Expression exp)
{
if (exp == null)
{
return null;
}
if (this.candidates.Contains(exp))
{
return this.Evaluate(exp);
}
return base.Visit(exp);
}
private Expression Evaluate(Expression e)
{
if (e.NodeType == ExpressionType.Constant)
{
return e;
}
LambdaExpression lambda = Expression.Lambda(e);
Delegate fn = lambda.Compile();
return Expression.Constant(fn.DynamicInvoke(null), e.Type);
}
}
/// <summary>
/// Performs bottom-up analysis to determine which nodes can possibly
/// be part of an evaluated sub-tree.
/// </summary>
class Nominator : ExpressionVisitor
{
Func<Expression, bool> fnCanBeEvaluated;
HashSet<Expression> candidates;
bool cannotBeEvaluated;
internal Nominator(Func<Expression, bool> fnCanBeEvaluated)
{
this.fnCanBeEvaluated = fnCanBeEvaluated;
}
internal HashSet<Expression> Nominate(Expression expression)
{
this.candidates = new HashSet<Expression>();
this.Visit(expression);
return this.candidates;
}
public override Expression Visit(Expression expression)
{
if (expression != null)
{
bool saveCannotBeEvaluated = this.cannotBeEvaluated;
this.cannotBeEvaluated = false;
base.Visit(expression);
if (!this.cannotBeEvaluated)
{
if (this.fnCanBeEvaluated(expression))
{
this.candidates.Add(expression);
}
else
{
this.cannotBeEvaluated = true;
}
}
this.cannotBeEvaluated |= saveCannotBeEvaluated;
}
return expression;
}
}
}
还有一种方法,可以在IQueryable<T>
而不是Expression
上调用
class Query<T> : IQueryable<T>
{
private IQueryProvider provider;
private Expression expression;
public Query(IQueryProvider provider, Expression expression)
{
this.provider = provider;
this.expression = expression;
}
public IEnumerator<T> GetEnumerator()
{
return ((IEnumerable<T>)this.Provider.Execute(this.Expression)).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)this.Provider.Execute(this.Expression)).GetEnumerator();
}
public Type ElementType
{
get { return typeof(T); }
}
public Expression Expression
{
get { return expression; }
}
public IQueryProvider Provider
{
get { return provider; }
}
}
public static IQueryable<T> Simplify<T>(this IQueryable<T> query)
{
return new Query<T>(query.Provider, Evaluator.PartialEval(query.Expression));
}
您现在可以写:
var identifier = new { id = 5 };
var query context.SomeMethod(i=>i.Id == identifier.Id)
.Simplify();
最终得到一个有效的查询:
context.SomeMethod(i=>i.Id == 5)
答案 1 :(得分:0)
C#不支持任何类型的匿名委托/表达式的显式捕获规则。
你的解决方法就是这样做。