首页 > 解决方案 > “参数未绑定在指定的 LINQ to Entities 查询表达式中。” 规格模式和

问题描述

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

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<T>该类在我的代码中的样子:

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

标签: c#entity-frameworklinqlinq-to-sqllinq-expressions

解决方案


问题出在你的ToExpression方法上。

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

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

您需要重写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]);

推荐阅读