【问题标题】:Specification Pattern with Expressions causing error - variable 'x' of type '' referenced from scope '', but it is not defined带有导致错误的表达式的规范模式 - 从范围 '' 引用的类型为 '' 的变量 'x',但未定义
【发布时间】:2017-11-29 10:31:13
【问题描述】:

我正在使用规范类来封装在我的域中使用的各种查询。

规范继承自 Specification<T> 基类:

public abstract class Specification<T> : ISpecification<T>
    where T : class
{
    public abstract Expression<Func<T, bool>> ToExpression();

    public virtual bool IsSatisfiedBy(T candidate)
    {
        var predicate = ToExpression().Compile();
        return predicate(candidate);
    }

    public Specification<T> And(Specification<T> specification)
    {
        return new AndSpecification<T>(this, specification);
    }

    public Specification<T> Or(Specification<T> specification)
    {
        return new OrSpecification<T>(this, specification);
    }
}

AndSpecification&lt;T&gt; 定义为:

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

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

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

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

        var param = leftExpression.Parameters.Single();

        return Expression.Lambda<Func<T, bool>>(
            andExpression, param);
    }
}

Specification&lt;T&gt; 类可以链接在一起,例如:

firstSpecification.And(secondSpecification).IsSatisfiedBy(candidate);

一个现实生活中的规范类的例子是:

public class IsAssignmentSetForStudentOverdueSpecification : Specification<Student>
{
    private readonly Assignment _assignment;

    public IsAssignmentSetForStudentOverdueSpecification(Assignment assignment)
    {
        _assignment = assignment
    }

    public override Expression<Func<Student, bool>> ToExpression()
    {

        var isStudentSetAssignment = new IsStudentSetAssignmentSpecification(_assignment);

        var hasAssignmentBeenCompletedByStudent = new HasAssignmentBeenCompletedByStudentSpecification(_assignment);

        return isStudentSetAssignment.And(hasAssignmentBeenCompletedByStudent).ToExpression();
    }
}

IsStudentSetAssignmentSpecificationToExpression() 方法如下(实际情况稍微复杂一些,但为简洁起见已简化):

public override Expression<Func<Student, bool>> ToExpression()
    {
        return x =>  _assignment.Students.Contains(x);
    }

'HasAssignmentBeenCompletedByStudentSpecificationhas aToExpression()`方法如下:

public override Expression<Func<Student, bool>> ToExpression()
    {
        return x =>  _assignment.StudentSubmissions.Contains(x);
    }

一个使用例子是:

var overdue = new IsAssignmentSetForStudentOverdueSpecification(assignment).IsSatisfiedBy(student);

其中assignmentstudent 是域类。但是,当我尝试使用它时,我得到以下System.InvalidOperationException

从范围“”引用的“学生”类型的变量“x”,但未定义

我该如何解决这个问题?

堆栈跟踪是:

在 System.Linq.Expressions.Expression.Property(表达式表达式,PropertyInfo 属性)\r\n 在 System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression 节点)\r\n 在 System.Linq.Expressions.ExpressionVisitor .VisitLambda[T](Expression1 node)\r\n at System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider nodes)\r\n at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)\r\n at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)\r\n at Beehive.Domain.Specifications.AndSpecification1.ToExpression() in C:\Beehive\2.0\Beehive.Domain\Specifications\AndSpecification.cs:第 35 行\r\n 在 Beehive.Domain.Planner.Assignments.Specifications。 IsAssignmentSetForStudentOverdueSpecification.ToExpression() 在 C:\Beehive\2.0\Beehive.Domain\Planner\Assignments\Specifications\IsAssignmentSetForStudentOverdueSpecification.cs:line 28\r\n at Beehive.Domain.Specifications.Specification1.IsSatisfiedBy(T candidate) in C:\\Beehive\\2.0\\Beehive.Domain\\Specifications\\Specification.cs:line 19\r\n at Beehive.Services.Shared.Planner.Assignments.AssignmentToAssignmentListViewMapHelper.Map(Assignment assignment, Student student) in C:\\Beehive\\2.0\\Beehive.Services\\Shared\\Planner\\Assignments\\AssignmentToAssignmentListViewMapHelper.cs:line 22\r\n at Beehive.Services.QueryHandlers.Planner.Assignments.AssignmentsForUserQueryHandler.&lt;&gt;c__DisplayClass5_0.&lt;CreateMap&gt;b__0(Assignment x) in C:\\Beehive\\2.0\\Beehive.Services\\QueryHandlers\\Planner\\Assignments\\AssignmentsForUserQueryHandler.cs:line 70\r\n at System.Linq.Enumerable.WhereSelectListIterator2.MoveNext()\ r\n 在 System.Collections.Generic.List1..ctor(IEnumerable1 集合)\r\n 在 C:\Beehive\2.0\Beehive 中的 System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)\r\n at Beehive.Services.QueryHandlers.Planner.Assignments.AssignmentsForUserQueryHandler.CreateMap(AssignmentToAssignmentListViewMapHelper mapHelper, Student student, IRepositoryCollection1 集合)。 Services\QueryHandlers\Planner\Assignments\AssignmentsForUserQueryHandler.cs:line 70\r\n at Beehive.Services.QueryHandlers.Planner.Assignmen ts.AssignmentsForUserQueryHandler.Handle(AssignmentsForUserQuery 查询) 在 C:\Beehive\2.0\Beehive.Services\QueryHandlers\Planner\Assignments\AssignmentsForUserQueryHandler.cs:line 59\r\n 在 System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1, TRet](CallSite 站点,T0 arg0,T1 arg1)\r\n 在 Beehive.DI.QueryProcessor.Process[TResult](IQuery1 query) in C:\\Beehive\\2.0\\Beehive.DI\\QueryProcessor.cs:line 26\r\n at Beehive.API.Controllers.Planner.AssignmentsController.&lt;&gt;c__DisplayClass1_0.&lt;GetAssignments&gt;b__0() in C:\\Beehive\\2.0\\Beehive.API\\Controllers\\Planner\\AssignmentsController.cs:line 26\r\n at Beehive.API.Controllers.ControllerBase.ExecuteQuery[TResult](Func1 操作)在 C:\Beehive\2.0\Beehive.API\Controllers\ControllerBase .cs:line 20\r\n 在 Beehive.API.Controllers.Planner.AssignmentsController.GetAssignments(Guid userId) 在 C:\Beehive\2.0\Beehive.API\Controllers\Planner\AssignmentsController.cs:line 26\r\ n 在 lambda_method(Closure , Object , Object[] )\r\n 在 System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.c__DisplayClass10.b__9(Object instance, Object[] methodParameters)\r\n 在 System. Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancelToken)\r\n---从先前引发异常的位置结束堆栈跟踪 ---\r\n 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\r \n 在 System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\r\n--- 从先前引发异常的位置结束堆栈跟踪 ---\r\n 在 System.Runtime.ExceptionServices。 ExceptionDispatchInfo.Throw()\r\n 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\r\n 在 System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r\n---从先前引发异常的位置结束堆栈跟踪 ---\r\n 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)\r \n 在 System.Web.Http.Controllers.ExceptionFilterResult.d__0.MoveNext()\r\n--- 结束从先前抛出异常的位置的堆栈跟踪 ---\r\n 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n 在 System.Web.Http.Controllers.ExceptionFilterResult.d__0.MoveNext()\ r\n--- 从先前引发异常的位置结束堆栈跟踪 ---\r\n 在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r\n 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification (任务任务)\r\n 在 System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()

【问题讨论】:

  • 是错误还是异常?哪一行代码导致了它?还有一个提示 - 如果可能,Compile 应该只调用一次。
  • 抱歉,这是System.InvalidOperationException。问题已更新。
  • 在您的AndSpecification.ToExpression() 中,左右表达式都有自己的参数。当你组合它们时,你在最终的 lambda 中使用左表达式的参数,所以右表达式的参数是悬空的,你有这个例外。第一条评论中的链接包含您修复该问题所需的所有信息。
  • 你应该返回_assignment.Students.Contains(x);在你的表达?

标签: c# .net linq lambda


【解决方案1】:

就像@Raphaël 和@Evk 建议的那样:

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

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

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

        var paramExpr = Expression.Parameter(typeof(T));

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

        andExpression = (BinaryExpression) new ParameterReplacer(paramExpr).Visit(andExpression);

        return Expression.Lambda<Func<T, bool>>(andExpression, paramExpr);
    }
}
public class ParameterReplacer : ExpressionVisitor
{
    private readonly ParameterExpression _parameter;

    protected override Expression VisitParameter(ParameterExpression node)
    {
        return base.VisitParameter(_parameter);
    }

    internal ParameterReplacer(ParameterExpression parameter)
    {
        _parameter = parameter;
    }
}

【讨论】:

    【解决方案2】:

    使用此处的代码https://petemontgomery.wordpress.com/2011/02/10/a-universal-predicatebuilder/,您可以创建正确组合两个表达式的 AndSpecification

    public class AndSpecification<T> : Specification<T>
        where T : class
    {
        private readonly Specification<T> _left;
        private readonly Specification<T> _right;
    
        public AndSpecification(Specification<T> left, Specification<T> right)
        {
            _right = right;
            _left = left;
        }
    
        public override Expression<Func<T, bool>> ToExpression()
        {
            var leftExpression = _left.ToExpression();
            var rightExpression = _right.ToExpression();
    
            return PredicateBuilder.And(leftExpression, rightExpression);
        }
    }
    

    这将修复它们,因此表达式的主体将使用相同的参数,并且应该避免您出错。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-09
      • 2011-06-08
      • 1970-01-01
      相关资源
      最近更新 更多