Linq查询:翻译局部变量引用

时间:2015-02-19 20:46:03

标签: c# linq

我有一种情况需要将linq查询中的局部变量引用转换为其值。 为此,我使用此类在查询中执行转换局部变量引用:

public static class Evaluator 
{
    public static Expression PartialEval(Expression expression, Func<Expression, bool> fnCanBeEvaluated) 
    {
        return new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(expression)).Eval(expression);
    }

    public static Expression PartialEval(Expression expression) 
    {
        return PartialEval(expression, Evaluator.CanBeEvaluatedLocally);
    }

    private static bool CanBeEvaluatedLocally(Expression expression)
    {
        return expression.NodeType != ExpressionType.Parameter;
    }

    class SubtreeEvaluator: ExpressionVisitor
    {
        HashSet<Expression> candidates;

        internal SubtreeEvaluator(HashSet<Expression> candidates) 
        {
            this.candidates = candidates;
        }

        internal Expression Eval(Expression exp) 
        {
            return this.Visit(exp);
        }

        protected 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);
        }
    }

    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;
        }

        protected 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;
        }
    }
} 

在翻译局部变量引用之后,使用如下:

public class DbQueryProvider : QueryProvider 
{
    // …

    private string Translate(Expression expression) 
    {
        expression = Evaluator.PartialEval(expression);
        return new QueryTranslator().Translate(expression);
    }
}

例如,如果我有这个表达式:

string ct = "London";
var query = db.Customers.Where(c => c.City == ct);

翻译后返回的表达式为:

SELECT * FROM (SELECT * FROM Customers) AS T WHERE (City = 'London')

代码工作正常,包含所有类型的变量,DateTime除外。 在我的数据库中,我只保存日期部分(没有时间),例如02/03/2012。

现在,如果我有这个问题:

DateTime dt1 = Convert.ToDateTime("01/01/2012");
var query = db.Orders.Where(c => c.dt == dt1);

如果我尝试翻译,则返回的表达式为:

SELECT * FROM (SELECT * FROM Orders) AS T WHERE (dt = '01/01/2012 12:00:00 AM')

正如您所看到的,翻译后的查询包含最后的时间,而我的查询每次都不会返回任何记录。

我该怎么办?

1 个答案:

答案 0 :(得分:-2)

var query = db.Orders.Where(c => c.dt == dt1.ToString("MM/dd/yyyy"));