在LINQ表达式exception中检测到循环

我收到错误:

在LINQ表达式中检测到循环。

尝试执行以下操作时在ToList()

 private IEnumerable FilterIdsByClient(IEnumerable entityIds) { entityIds = MyObjectContext.CreateObjectSet() .Where(x => x.ClientId == _clientId) .Where(x => entityIds.Contains(x.Id)) .Select(x => x.Id); return entityIds.ToList(); } 

但是,这不会抛出任何exception并且工作正常:

 private IEnumerable FilterIdsByClient(IEnumerable entityIds) { entityIds = MyObjectContext.CreateObjectSet() .Where(x => x.ClientId == _clientId) .Where(x => entityIds.Contains(x.Id)) .Select(x => x.Id) .ToList(); return entityIds; } 

(当然这是一个简单的版本)。

任何人都知道为什么会发生这种奇怪的行为?

编辑:

这是堆栈跟踪:

  at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.Visit(Expression exp) at System.Data.Objects.ELinq.Funcletizer.Funcletize(Expression expression, Func`1& recompileRequired) at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.InlineExpression(Expression exp) at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.InlineObjectQuery(ObjectQuery inlineQuery, Type expressionType) at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.InlineValue(Expression expression, Boolean recompileOnChange) at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.Visit(Expression exp) at System.Linq.Expressions.EntityExpressionVisitor.VisitExpressionList(ReadOnlyCollection`1 original) at System.Linq.Expressions.EntityExpressionVisitor.VisitMethodCall(MethodCallExpression m) at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp) at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.Visit(Expression exp) at System.Linq.Expressions.EntityExpressionVisitor.VisitLambda(LambdaExpression lambda) at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp) at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.Visit(Expression exp) at System.Linq.Expressions.EntityExpressionVisitor.VisitUnary(UnaryExpression u) at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp) at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.Visit(Expression exp) at System.Linq.Expressions.EntityExpressionVisitor.VisitExpressionList(ReadOnlyCollection`1 original) at System.Linq.Expressions.EntityExpressionVisitor.VisitMethodCall(MethodCallExpression m) at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp) at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.Visit(Expression exp) at System.Linq.Expressions.EntityExpressionVisitor.VisitExpressionList(ReadOnlyCollection`1 original) at System.Linq.Expressions.EntityExpressionVisitor.VisitMethodCall(MethodCallExpression m) at System.Linq.Expressions.EntityExpressionVisitor.Visit(Expression exp) at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.Visit(Expression exp) at System.Data.Objects.ELinq.Funcletizer.Funcletize(Expression expression, Func`1& recompileRequired) at System.Data.Objects.ELinq.ExpressionConverter..ctor(Funcletizer funcletizer, Expression expression) at System.Data.Objects.ELinq.ELinqQueryState.CreateExpressionConverter() at System.Data.Objects.ELinq.ELinqQueryState.GetExecutionPlan(Nullable`1 forMergeOption) at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption) at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable.GetEnumerator() at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) at ...FilterIdsByClient... 

EDIT2:

应该注意,在这种情况下, IEnumerable entityIds是一个来自ajax请求的列表,而不是来自某个地方的查询。

这种行为看起来很奇怪,因为你没有正确地考虑闭包语义。 请参阅以下评论:

 private IEnumerable FilterIdsByClient(IEnumerable entityIds) { // The variable entityIds points to whatever was passed in: A List, according to the edited question. entityIds = //this is an assignment, changing the referent of entityIds MyObjectContext.CreateObjectSet() .Where(x => x.ClientId == _clientId) .Where(x => entityIds.Contains(x.Id)) //this lambda closes over the variable entityIds .Select(x => x.Id); // The query now has a reference to the *variable* entityIds, not to the object that entityIds pointed to originally. // The value of entityIds has been changed; it now points to the query itself! // The query is therefore operating on itself; this causes the "cycle detected" message. // Because of delayed execution, the query is not executed until the next line of code: return entityIds.ToList(); } 

为什么要分配参数? 为什么不

 private IEnumerable FilterIdsByClient(IEnumerable entityIds) { return MyObjectContext.CreateObjectSet() .Where(x => x.ClientId == _clientId) .Where(x => entityIds.Contains(x.Id)) .Select(x => x.Id) .ToList(); } 

答案是不将LINQ查询分配给entityIds 。 请参阅@ Stu的答案以获得解决方案。

当然有一个循环。 您在Where Linq Extension方法中使用entityIds,它是自己构造的查询。 而不是修改输入的IEnumerable,返回一个新的查询,如下所示:

 private IEnumerable FilterIdsByClient(IEnumerable entityIds) { var query = MyObjectContext.CreateObjectSet() .Where(x => x.ClientId == _clientId) .Where(x => entityIds.Contains(x.Id)) .Select(x => x.Id); return query.ToList(); }