强制.NET表达式使用当前值 [英] Force a .NET Expression to Use Current Value

查看:74
本文介绍了强制.NET表达式使用当前值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试找出是否有一种方法可以强制C#表达式将表达式的一部分转换为值.我正在调用接受定义查询的表达式的方法.我在标识查询的对象中有一系列值.这是一个简化的示例:

I am trying to figure out if there is a way to force a C# expression to convert part of the expression to a value. I am calling a method that accepts an expression that defines a query. I have a series of values in an object that identifies the query. Here is a simplified example:

var identifier = new { id = 5 };
context.SomeMethod(i=>i.Id == identifier.Id);

这失败.基于该错误,我看到该表达式似乎正在尝试将"identifier.Id"合并到表达式中,而不是将"identifier.Id"解析为其值为5.以下代码有效:

This fails. Based on the error I'm seeing it looks like the expression is attempting to incorporate "identifier.Id" into the expression instead of resolving "identifier.Id" to its value, which is 5. The following code works:

var id = 5;
context.SomeMethod(i=>i.Id == id)

尽管这可行,但这是一个简化的示例,在我的实际代码中,这将很痛苦.所以我的问题是,是否可以使用某些语法来包装表达式的一部分以强制其解析为值?

Although this works, this is a simplified example and in my actual code this would be painful. So my question is, is there some syntax you can use to wrap part of an expression to force it to resolve to a value?

推荐答案

In this blog entry it discusses how one can simplify an Expression. It does a two-pass approach in which it first marks all of the nodes that aren't parameters and don't have and parameters as children, and then it does another pass where it evaluates those nodes so that anything that can be computed without relying on the parameter is evaluated.

以下是代码的一些小调整:

Here is the code with a few minor tweaks:

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

And an additional method so that it can be called on an IQueryable<T> rather than an 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();

最后得到一个有效的查询:

And end up with a query that is effectively:

context.SomeMethod(i=>i.Id == 5)

这篇关于强制.NET表达式使用当前值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆