【问题标题】:Dividing an Expression in C# using Expression.AndAlso() causes an Exception使用 Expression.AndAlso() 在 C# 中划分表达式会导致异常
【发布时间】:2015-06-18 14:25:00
【问题描述】:

在我用 C# 编写的项目中,我发现在这种 linq 方法中使用了一个巨大的谓词:

public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

这个谓词完美地工作,但它的条件如此之多,以至于我在理解它之前做了很多努力。我想让它可读。所以我写了几个Expression。

但我有一个像这样的运行时异常:The dreaded "parameter was not bound in the specified LINQ to Entities query expression" exception

我想尝试答案,但我仍然不明白为什么参数 (c) 有问题,请参阅:

// in a method
Func<string, Expression<Func<TEntity, bool>>> expr1 = (query) => return (c) => ... ;
Func<string, Expression<Func<TEntity, bool>>> expr2 = (query) => return  (c) => ... ;

var expr = Expression.AndAlso(expr1("a string").Body, expr2("same string").Body);

return Expression.Lambda<Func<TEntity, bool>>(expr , expr1("a string").Parameters[0]);

我的问题是理解为什么会在最后我恢复到巨大的谓词时发生这个异常。

【问题讨论】:

    标签: c# linq lambda expression


    【解决方案1】:

    因为您看到一个c 参数,实际上有两个不同的c 参数(我们称它们为c1c2)。因此,当您合并这两个表达式时:

    c1 => c1.Something && c2.SomethingElse;
    

    CLR 很生气,因为它找不到 c2

    更糟糕的是,当您编写代码时,您有 三个 c!

    c3 => c1.Something && c2.SomethingElse
    

    这是因为您重建了两次expr1("a string")(在Expression.AndAlso(expr1("a string").Bodyexpr1("a string").Parameters[0] 中)!

    你应该保存它!

    var temp1 = expr1("a string");
    var temp2 = expr2("same string");
    
    var expr = Expression.AndAlso(temp1.Body, temp2.Body);
    
    // now fix the expr so that it uses the parameters of temp1
    
    return Expression.Lambda<Func<TEntity, bool>>(expr, temp1.Parameters);
    

    举个清楚的例子:

    var temp1a = expr1("a string");
    var temp1b = expr1("a string");
    var temp2 = expr2("same string");
    
    Console.WriteLine(temp1a.Parameters[0] == temp1b.Parameters[0]); // False
    Console.WriteLine(temp1a.Parameters[0] == temp2.Parameters[0]); // False
    

    现在...我的参数替换器版本:

    public class SimpleParameterReplacer : ExpressionVisitor
    {
        public readonly ReadOnlyCollection<ParameterExpression> From;
        public readonly ReadOnlyCollection<ParameterExpression> To;
    
        public SimpleParameterReplacer(ParameterExpression from, ParameterExpression to)
            : this(new[] { from }, new[] { to })
        {
        }
    
        public SimpleParameterReplacer(IList<ParameterExpression> from, IList<ParameterExpression> to)
        {
            if (from == null || from.Any(x => x == null))
            {
                throw new ArgumentNullException("from");
            }
    
            if (to == null || to.Any(x => x == null))
            {
                throw new ArgumentNullException("to");
            }
    
            if (from.Count != to.Count)
            {
                throw new ArgumentException("to");
            }
    
            // Note that we should really clone from and to... But we will
            // ignore this!
            From = new ReadOnlyCollection<ParameterExpression>(from);
            To = new ReadOnlyCollection<ParameterExpression>(to);
        }
    
        protected override Expression VisitParameter(ParameterExpression node)
        {
            int ix = From.IndexOf(node);
    
            if (ix != -1)
            {
                node = To[ix];
            }
    
            return base.VisitParameter(node);
        }
    }
    

    你可以用来改变单个参数或参数数组...你可以像这样使用它:

    var temp1 = expr1("a string");
    var temp2 = expr2("same string");
    
    var expr = Expression.AndAlso(temp1.Body, temp2.Body);
    expr = new SimpleParameterReplacer(temp2.Parameters, temp1.Parameters).Visit(expr);
    
    return Expression.Lambda<Func<TEntity, bool>>(expr, temp1.Parameters);
    

    【讨论】:

    • 这是在解释问题,但不能帮助他解决问题。
    • @Servy 我想尝试答案,但我仍然不明白为什么参数 (c) 是一个问题,请参阅:我的问题是要理解为什么这个异常发生在最后我恢复到巨大的谓词时。他已经有了回应......但它不明白问题是什么
    • 感谢您的解释。我仍然有同样的例外,所以我想我现在还有两个“c”!
    • @xanatos :确实我想了解这个问题。我稍后会花时间处理这个问题。
    • @AMS 我已经添加了我的参数替换器版本以及如何使用它的示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-15
    • 2018-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多