【问题标题】:ToList thows Exception when building Expression tree using concat multiple Expressions使用 concat 多个表达式构建表达式树时,ToList 抛出异常
【发布时间】:2016-08-17 01:40:30
【问题描述】:

我正在构建一个例程来连接多个实体,以便按字符串进行过滤。我使用以下代码创建我的表达式,因为我使用相同的代码进行排序,所以它可以正常工作。

private void SetExpression<T>(string property, ref Type type, ref ParameterExpression arg, ref Expression expr)
{
    string[] props = property.Split('.');
    foreach (string prop in props)
    {
        PropertyInfo pi = type.GetProperty(prop);
        expr = Expression.Property(expr, pi);
        type = pi.PropertyType;
    }
}

然后我使用另外 2 种方法来构建树。我使用 GenerateConcat 来制作连接部分。

private Expression<Func<T, string>> GenerateConcat(ParameterExpression parameterExpression, params Expression[] expressions)
{
    var parameter = parameterExpression;
    var separator = Expression.Constant(" ");
    var concatArgs = expressions;

    var concatCall = Expression.Call(typeof(string).GetMethod("Concat", new[] { typeof(string), typeof(string), typeof(string) }), concatArgs);
    return Expression.Lambda<Func<T, string>>(concatCall, parameter);
}

我使用 GenerateContains 来创建 contains 方法。

private Expression<Func<T, bool>> GenerateContains(Expression<Func<T, string>> member, string value)
{
    var containsCall = Expression.Call(member.Body, "Contains", Type.EmptyTypes, Expression.Constant(value));
    return Expression.Lambda<Func<T, bool>>(containsCall, member.Parameters);
}

运行时,一切正常,我的谓词看起来正确形成。但是,当调用 ToList() 时会引发以下异常。

System.InvalidOperationException: The parameter 'x' was not bound in the specified LINQ to Entities query expression.

Result StackTrace:  
at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.ParameterTranslator.TypedTranslate(ExpressionConverter parent, ParameterExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MemberAccessTranslator.TypedTranslate(ExpressionConverter parent, MemberExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MemberAccessTranslator.TypedTranslate(ExpressionConverter parent, MemberExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.StringTranslatorUtil.ConvertToString(ExpressionConverter parent, Expression linqExpression)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.StringTranslatorUtil.<>c__DisplayClass1c.<ConcatArgs>b__1a(Expression arg)
   at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
   at System.Linq.Buffer`1..ctor(IEnumerable`1 source)
   at System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.StringTranslatorUtil.ConcatArgs(ExpressionConverter parent, Expression linq, Expression[] linqArgs)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.StringConcatTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateFunctionIntoLike(MethodCallExpression call, Boolean insertPercentAtStart, Boolean insertPercentAtEnd, Func`5 defaultTranslator)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.StringContainsTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.BinaryTranslator.TypedTranslate(ExpressionConverter parent, BinaryExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateLambda(LambdaExpression lambda, DbExpression input, DbExpressionBinding& binding)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, DbExpression& source, DbExpressionBinding& sourceBinding, DbExpression& lambda)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.OneLambdaTranslator.Translate(ExpressionConverter parent, MethodCallExpression call)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.SequenceMethodTranslator.Translate(ExpressionConverter parent, MethodCallExpression call, SequenceMethod sequenceMethod)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.MethodCallTranslator.TypedTranslate(ExpressionConverter parent, MethodCallExpression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TypedTranslator`1.Translate(ExpressionConverter parent, Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.TranslateExpression(Expression linq)
   at System.Data.Entity.Core.Objects.ELinq.ExpressionConverter.Convert()
   at System.Data.Entity.Core.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__6()
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()
   at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at Triton.Repository.Test.UnitTest1.ComplexTypeTest() in C:\BitBucket\triton-mvc-5\Triton\Triton.Repository.Test\UnitTest1.cs:line 56
Result Message: 
Test method Triton.Repository.Test.UnitTest1.ComplexTypeTest threw exception: 
System.InvalidOperationException: The parameter 'x' was not bound in the specified LINQ to Entities query expression.

我猜,但问题是我如何构建 Concat 部分 (GenerateConcat),但我似乎无法弄清楚出了什么问题。我是否遗漏了关于 ParameterExpression 的任何内容?我确保所有表达式都相同。提前致谢。

编辑 1:这是创建表达式的代码

我使用 Initialize 块根据属性名称创建表达式。然后我使用 Append 方法从其他属性中获取表达式。我的错误是当我创建子属性时我没有正确分配 ParameterExpression。所以我重载了 Intialize 并添加了一个 ParamerterExpression 参数以保持所有子属性的表达式相同。这样就解决了。

public ExpressionBuilder Initialize<T>(string property)
{
    if (string.IsNullOrEmpty(property))
        throw new ArgumentNullException(property);

    Type type = typeof(T);
    ParameterExpression arg = Expression.Parameter(type, "x");
    Expression expr = arg;

    SetExpression<T>(property, ref type, ref arg, ref expr);

    ExpressionModel = new ExpressionModel() { Expression = expr, ParameterExpression = arg, Type = type };

    IsIntialized = true;
    return this;
}



public ExpressionBuilder Append<T>(string property)
{
    if (string.IsNullOrEmpty(property))
        throw new ArgumentNullException(property);

    if (IsIntialized == false)
        throw new Exception("ExpressionBuilder has not been initialized.");

    Type type = ExpressionModel.Type;
    ParameterExpression arg = ExpressionModel.ParameterExpression;
    Expression expr = ExpressionModel.Expression;

    SetExpression<T>(property, ref type, ref arg, ref expr);
    ExpressionModel.Expression = expr;
    ExpressionModel.ParameterExpression = arg;
    ExpressionModel.Type = type;

    return this;
}

【问题讨论】:

  • 我很难过,但如果你包含调用代码可能会对其他人有所帮助,这可能是x 的来源?
  • GenerateConcat 有问题(您使用 3 个字符串参数绑定到硬编码重载),但这不会导致此特定异常。您有一些未绑定的参数x(很可能来自外部 lambda)。如果没有重现问题的调用代码,很难准确判断。
  • 感谢您指出正确的方向。

标签: c# linq linq-to-entities expression-trees


【解决方案1】:

子属性的 ParameterExpression 必须与父属性相同。因此,我重载了创建 Expression 的函数,以便能够显式设置 ParameterExpression。

public ExpressionBuilder Initialize<T>(string property, ParameterExpression parameterExpression)
{
    if (string.IsNullOrEmpty(property))
        throw new ArgumentNullException(property);

    Type type = typeof(T);
    ParameterExpression arg = parameterExpression;
    Expression expr = arg;

    SetExpression<T>(property, ref type, ref arg, ref expr);

    ExpressionModel = new ExpressionModel() { Expression = expr, ParameterExpression = arg, Type = type };

    IsIntialized = true;
    return this;
}

【讨论】:

    猜你喜欢
    • 2014-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多