c# - “参数未绑定在指定的 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());
}
}
解决方案
问题出在你的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]);
推荐阅读
- clang - 区分用户程序中的系统调用
- javascript - React Native 状态管理 - useState 不会在单个操作上更改状态,而是在多个操作上更改状态
- flutter - 如何在颤动中生成流
- javascript - 如何将文本插入word并将其转换为pdf
- javascript - 状态不更新内部递归
- python - numpy 无法在 Big Sur 11.4 上运行
- php - 尝试读取字符串错误 Codeigniter 上的属性
- grails - 未捕获的类型错误:无法设置未定义的属性“PieRenderer”
- sql - 检索各部门薪水较高的员工姓名
- mongoose - findOneAndUpdate 更新不正确的文档