具有导致错误的表达式的规范模式 - 从作用域引用的类型变量'x',但未定义



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

规范继承自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<T>定义为:

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<T>类链接在一起,例如:

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

IsStudentSetAssignmentSpecification有它ToExpression()方法如下(真正的事情有点复杂,但为了简洁起见,它已被简化):

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

'HasAssignmentBeenCompleteByStudentSpecificationhas 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",但未定义

我该如何解决这个问题?

堆栈跟踪为:

at System.Linq.Expressions.Expression.Property(Expression expression, PropertyInfo 属性)\r at System.Linq.Expressions.ExpressionVisitor.VisitBinary(

BinaryExpression node)\r at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression1 node)rn at System.Linq.Expressions.ExpressionVisitor.VisitArguments(IArgumentProvider nodes)rn at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)rn at System.Linq.Expressions.ExpressionVisitor.VisitBinary(BinaryExpression node)rn at Beehive.Domain.Specifications.AndSpecification1.ToExpression() in C:\Beehive\2.0\Beehive.Domain\Specification\AndSpecification.cs:line 35\r atBeehive.Domain.Planner.Assignments.Specification.IsAssignmentSetForStudentOverdueSpecification.ToExpression() in C:\Beehive\2.0\Beehive.Domain\Planner\Assignments\Specification\IsAssignmentSetForStudentOverdueSpecification.cs:line 28\r at Beehive.Domain.Specification.Specification1.IsSatisfiedBy(T candidate) in C:\Beehive\2.0\Beehive.Domain\Specifications\Specification.cs:line 19rn 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 22rn at Beehive.Services.QueryHandlers.Planner.Assignments.AssignmentsForUserQueryHandler.<>c__DisplayClass5_0.<CreateMap>b__0(Assignment x) in C:\Beehive\2.0\Beehive.Services\QueryHandlers\Planner\Assignments\AssignmentsForUserQueryHandler.cs:line 70rn at System.Linq.Enumerable.WhereSelectListIterator2.MoveNext()\r at System.Collections.Generic.List1..ctor(IEnumerable1 collection)\r at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)rn at Beehive.Services.QueryHandlers.Planner.Assignments.AssignmentsForUserQueryHandler.CreateMap(AssignmentToAssignmentListViewMapHelper mapHelper, Student student, IRepositoryCollection1 collection) in C:\Beehive\2.0\Beehive.Services\QueryHandlers\Planner\Assignments\AssignmentsForUserQueryHandler.cs:line 70\r at Beehive.Services.QueryHandlers.Planner.Assignments.AssignmentsForUserQueryHandler.Handle(AssignmentsForUserQuery query) in C:\Beehive\2.0\Beehive.Services\QueryHandlers\Planner\Assignments\AssignmentsForUserQueryHandler.cs:line 59\r at System.Dynamic.UpdateDelegates.UpdateAndExecute2[T0,T1,TRet](CallSite site, T0 arg0, T1 arg1)\r at Beehive.DI.QueryProcessor.Process[TResult](IQuery1 query) in C:\Beehive\2.0\Beehive.DI\QueryProcessor.cs:line 26rn at Beehive.API.Controllers.Planner.AssignmentsController.<>c__DisplayClass1_0.<GetAssignments>b__0() in C:\Beehive\2.0\Beehive.API\Controllers\Planner\AssignmentsController.cs:line 26rn at Beehive.API.Controllers.ControllerBase.ExecuteQuery[TResult](Func1 action) in C:\Beehive\2.0\Beehive.API\Controllers\ControllerBase.cs:line 20\r at Beehive.API.Controllers.Planner.AssignmentsController.GetAssignments(Guid userId) in C:\Beehive\2.0\Beehive.API\Controllers\Planner\AssignmentsController.cs:line 26\r at lambda_method(Closure , Object , Object[] )\r at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.b__9(Object instance, Object[] methodParameters)\r at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary'2 arguments, CancelToken cancelToken)\r--- 从引发异常的先前位置的堆栈跟踪结束 ---\r at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r at System.Web.Http.Controllers.ApiControllerActionInvoker.d__0.MoveNext()\r--- 在系统运行时引发异常的先前位置的堆栈跟踪结束---\r 在系统.运行时。异常服务.异常调度信息.Throw()\r 在系统.运行时.编译器服务.任务等待者.手柄不成功和调试器通知(任务任务)\r 在 System.Web.Http.Controllers.ActionFilterResult.d__2.MoveNext()\r--- 从引发异常的先前位置开始的堆栈跟踪结束 ---\r 在System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r at System.Web.Http.Controllers.ExceptionFilterResult.d__0.MoveNext()\r--- 从引发异常的先前位置开始的堆栈跟踪结束 ---\r at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r at System.Web.Http.Controllers.ExceptionFilterResult.d__0.MoveNext()\r--- End从先前---位置的堆栈跟踪,其中在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()\r 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r 在 System.Web.Http.Dispatcher.HttpControllerDispatcher.d__1.MoveNext()

像@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;
}
}

使用此处的代码 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);
}
}

这将修复它们,以便表达式的主体将使用相同的参数,并应避免您的错误。

最新更新