【发布时间】: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<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);
}
'HasAssignmentBeenCompletedByStudentSpecificationhas aToExpression()`方法如下:
public override Expression<Func<Student, bool>> ToExpression()
{
return x => _assignment.StudentSubmissions.Contains(x);
}
一个使用例子是:
var overdue = new IsAssignmentSetForStudentOverdueSpecification(assignment).IsSatisfiedBy(student);
其中assignment 和student 是域类。但是,当我尝试使用它时,我得到以下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](Expression
1 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.<>c__DisplayClass5_0.<CreateMap>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.<>c__DisplayClass1_0.<GetAssignments>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);在你的表达?