【问题标题】:A cycle was detected in a LINQ expression exception在 LINQ 表达式异常中检测到循环
【发布时间】:2011-12-28 16:08:26
【问题描述】:

我得到了错误:

在 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...

编辑2:

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

【问题讨论】:

  • 您可以尝试将MyObjectContext.CreateObjectSet&lt;TEntity&gt;() 的结果解析为一个变量,然后在LINQ 查询中使用它吗?
  • 但这会从数据库返回表,不是吗?还有,这有什么帮助?

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


【解决方案1】:

这种行为看起来很奇怪,因为您没有正确考虑闭包语义。请参阅下面的 cmets:

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

【讨论】:

    【解决方案2】:

    你为什么要分配给你的参数?为什么不

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

    【讨论】:

    • 这不是我的问题。我知道我可以做到这一点,我同意这样做更好,但我想知道为什么会发生这种行为。
    • 当然,只是想指出分配您的方式是自找麻烦,期间。解释很简单:您正在分配一个使用原始值的枚举器,正如 phoog 解释的那样。
    【解决方案3】:

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

    【讨论】:

    • 请注意,entityIds 是一个列表,而不是一个查询。你重写它是什么意思?怎么样?
    • @gil:实际上,entityIds 是一个变量。在引发异常的代码示例中,在 .ToList() 调用中,该变量已被重新分配并指向查询,而不是列表。
    • 在您的第一个示例中,它不是一个列表,而是一个 LINQ 查询。这是错误解释的。但是,在第二个示例中,ToList 的输出在查询运行之后分配给entityIds,因此entityIds 将是null。关于如何重写它:我无法帮助您,因为这取决于您要完成的工作。
    • @Pieter:这个问题不涉及null。相反,entityIds 将指向通过变量 entityIds 的闭包(直接或间接)引用自身的 WhereIterator 或 SelectIterator(或类似的)。这就是为什么您会收到循环错误而不是 NullReferenceException。
    • 为什么会得到支持?问题分析不正确。
    【解决方案4】:

    当然有一个循环。您在 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();
    }
    

    【讨论】:

    • 但 entityIds 只是一个列表。不是查询。
    • @gil: 实际上,entityIds 是一个变量。在引发异常的代码示例中,在.ToList() 调用中,该变量已被重新分配并指向查询,而不是列表。
    • @gil 查看 phoog 的回答。如果您了解 Linq 表达式使用具有闭包的 lambda,因此您会受益匪浅。
    猜你喜欢
    • 2017-04-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-21
    • 1970-01-01
    • 1970-01-01
    • 2016-08-07
    • 1970-01-01
    相关资源
    最近更新 更多