【问题标题】:"The parameter was not bound in the specified LINQ to Entities query expression." Specification Pattern And“参数未绑定在指定的 LINQ to Entities 查询表达式中。”规格模式和
【发布时间】:2020-06-11 19:10:04
【问题描述】:

我遵循here 中描述的规范模式实现。我有一个如下所示的存储库方法:

public IEnumerable<MyDto> Find(Specification<Dto> specification)
{
    return myDbContext.MyDtos.Where(specification.ToExpression()).Take(20).ToList();
}

如果我使用普通的非复合规范,它可以正常工作,但以下场景会失败并显示消息“参数 'r' 未绑定在指定的 LINQ to Entities 查询表达式中。”:

Specification<MyDto> spec = new Spec1(someCriterion)
    .And(new Spec2(someCriterion))
    .And(new Spec3(someCriterion))
    // etc...

var myDtos = repo.Find(spec);

从我目前所读到的内容来看,这与所有表达式的参数引用不同,但我不确定如何解决此问题。

作为参考,这是AndSpecification&lt;T&gt; 类在我的代码中的样子:

public class AndSpecification<T> : Specification<T>
{
    private readonly Specification<T> _left;
    private readonly Specification<T> _right;

    public AndSpecification(Specification<T> left, Specification<T> right)
    {
        _left = left;
        _right = right;
    }

    public override Expression<Func<T, bool>> ToExpression()
    {
        Expression<Func<T, bool>> leftExpression = _left.ToExpression();
        Expression<Func<T, bool>> rightExpression = _right.ToExpression();

        BinaryExpression andExpression = Expression.AndAlso(
            leftExpression.Body, rightExpression.Body);

        return Expression.Lambda<Func<T, bool>>(
            andExpression, leftExpression.Parameters.Single());
    }
}

【问题讨论】:

  • 哪行代码抛出异常?你向我们展示了那行代码吗?
  • 如果您包含包含抛出的行的代码部分以及异常的完整堆栈跟踪,这可能会有所帮助。
  • 它使用的参数是什么?实体属性是什么?
  • 感谢您的 cmets。请检查已接受的解决方案答案。每个规范的表达式使用的参数是 DTO 本身。问题在于,尽管名称相同,但表达式中的参数并不相同。解决方案是确保在所有链接的表达式中都使用左端表达式的参数。

标签: c# entity-framework linq linq-to-sql linq-expressions


【解决方案1】:

问题出在您的ToExpression 方法中。

leftExpression 和 rightExpression 都是 LambdaExpression,并且每个都有自己不同的 T 参数。

当您创建从 ToExpression 返回的 LambdaExpression 时,您说这应该使用来自 leftExpression 的参数。但是rightExpression 中使用的参数呢? rightExpression.Body 包含使用rightExpression.Parameters[0] 的表达式,即使您将rightExpression.Body 放入另一个表达式中,它们仍将继续引用对象rightExpression.Parameters[0]。

您需要重写rightExpression 以使用与leftExpression 相同的参数。最简单的方法是使用ExpressionVisitor。

首先,创建一个ExpressionVisitor,它只是将一个参数替换为另一个参数:

public class ParameterReplaceVisitor : ExpressionVisitor
{
    private readonly ParameterExpression target;
    private readonly ParameterExpression replacement;

    public ParameterReplaceVisitor(ParameterExpression target, ParameterExpression replacement) =>
        (this.target, this.replacement) = (target, replacement);

    protected override Expression VisitParameter(ParameterExpression node) =>
        node == target ? replacement : base.VisitParameter(node);
}

然后用它来重写你的rightExpression.Body,所以它使用与leftExpression相同的参数对象:

var visitor = new ParameterReplaceVisitor(rightExpression.Parameters[0], leftExpression.Parameters[0]);
var rewrittenRightBody = visitor.Visit(rightExpression.Body.Visit);
var andExpression = Expression.AndAlso(leftExpression.Body, rewrittenRightBody);

return Expression.Lambda<Func<T, bool>>(
    andExpression, leftExpression.Parameters[0]);

【讨论】:

  • 一件事,不应该是visitor.Visit(rightExpression.Body)吗?
猜你喜欢
  • 1970-01-01
  • 2019-05-16
  • 2014-10-19
  • 1970-01-01
  • 1970-01-01
  • 2012-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多