用变量的常量动态生成lambda表达式 [英] Dynamically generate lambda expression with constants from variables

查看:137
本文介绍了用变量的常量动态生成lambda表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经解决了一些异常的问题,一些EF 6代码,即使在2ms内执行底层查询时,与Oracle.ManagedDataAccess.Client驱动程序一起运行的查询有时也需要几分钟的时间才能返回结果。一个示例查询将如下所示:

I've been troubleshooting an unusual issue with some EF 6 code where queries running with the Oracle.ManagedDataAccess.Client driver are sometimes taking several minutes to return results even when the underlying query executes within 2ms. An example query would be as follows:

var result = users.Where(u => u.username == varUserName).FirstOrDefault();

此查询可能需要几分钟的时间才能返回,但是如果将查询与相同的内容替换为在lambda函数中不变,它会立即运行:

This query might take several minutes to return, however if I replace the query with the same thing with a constant in the lambda function, it runs instantaneously:

var result = users.Where(u => u.username == "testUsername").FirstOrDefault();

要解决此问题,我可以编写参数化的SQL查询,也可以手动生成适当的lambda表达式树:

To work around this issue, I can either write parameterised SQL queries, or I can manually generate an appropriate lambda expression tree:

var userParam = Expression.Parameter(typeof(Entity.User), "user");
var userNameField = Expression.Property(userParam, "username");
var userNameConstant = Expression.Constant(varUserName, typeof(string));
var equalUserName = Expression.Equal(userNameField, userNameConstant);
var lambda = Expression.Lambda<Func<Entity.User, bool>>(equalUserName, new ParameterExpression[] { userParam });
var result = users.Where(lambda).FirstOrDefault();

因为这样做,它提出了一个问题:有一种方法可以轻松地生成lambda表达式导致变量直接包含在常量中的树,而不是引用变量?

Because this works, it begs the question: is there a way to easily generate lambda expression trees which result in variables being directly included as constants, instead of references to variables?

例如,这样的东西是理想的:

For example, something like this would be ideal:

var lambdaExpression = (u => u.username == varUserName).ReplaceVariablesWithConstants();


推荐答案

可以使用 ExpressionVisitor 它评估 ConstantExpression 这样的成员:

It can be done relatively easy with ExpressionVisitor which evaluates the ConstantExpression members like this:

public static class ExpressionUtils
{
    public static Expression<TDelegate> ReplaceVariablesWithConstants<TDelegate>(this Expression<TDelegate> source)
    {
        return source.Update(
            new ReplaceVariablesWithConstantsVisitor().Visit(source.Body), 
            source.Parameters);
    }

    class ReplaceVariablesWithConstantsVisitor : ExpressionVisitor
    {
        protected override Expression VisitMember(MemberExpression node)
        {
            var expression = Visit(node.Expression);
            if (expression is ConstantExpression)
            {
                var variable = ((ConstantExpression)expression).Value;
                var value = node.Member is FieldInfo ?
                    ((FieldInfo)node.Member).GetValue(variable) :
                    ((PropertyInfo)node.Member).GetValue(variable);
                return Expression.Constant(value, node.Type);
            }
            return node.Update(expression);
        }
    }
}

这篇关于用变量的常量动态生成lambda表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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