在LINQ表达式异常中检测到循环

时间:2011-12-28 16:08:27

标签: c# .net entity-framework entity-framework-4

我收到错误:

  

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

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

private IEnumerable<int> FilterIdsByClient(IEnumerable<int> entityIds)
{
    entityIds =
        MyObjectContext.CreateObjectSet<TEntity>()
            .Where(x => x.ClientId == _clientId)
            .Where(x => entityIds.Contains(x.Id))
            .Select(x => x.Id);

    return entityIds.ToList();
}

然而,这不会抛出任何异常并且工作正常:

private IEnumerable<int> FilterIdsByClient(IEnumerable<int> entityIds)
{
    entityIds =
        MyObjectContext.CreateObjectSet<TEntity>()
            .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<T>.GetEnumerator()
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at ...FilterIdsByClient...

EDIT2:

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

4 个答案:

答案 0 :(得分:11)

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

private IEnumerable<int> FilterIdsByClient(IEnumerable<int> 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<TEntity>() 
            .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();
}

答案 1 :(得分:6)

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

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

答案 2 :(得分:4)

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

答案 3 :(得分:3)

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

private IEnumerable<int> FilterIdsByClient(IEnumerable<int> entityIds)
{
    var query =
        MyObjectContext.CreateObjectSet<TEntity>()
            .Where(x => x.ClientId == _clientId)
            .Where(x => entityIds.Contains(x.Id))
            .Select(x => x.Id);

    return query.ToList();
}