Un cycle a été détecté dans une exception d’expression LINQ

Je reçois l’erreur:

Un cycle a été détecté dans une expression LINQ.

dans ToList() en essayant de faire ce qui suit:

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

Ceci ne lance cependant aucune exception et fonctionne bien:

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

(Ceci est une version simplifiée, bien sûr).

Quelqu’un sait-il pourquoi ce comportement étrange se produit?

Modifier:

C’est la trace de la stack:

  at System.Data.Objects.ELinq.Funcletizer.FuncletizingVisitor.Visit(Expression exp) at System.Data.Objects.ELinq.Funcletizer.Funcletize(Expression expression, Func`1& recomstackRequired) 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 recomstackOnChange) 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& recomstackRequired) 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:

À noter que dans ce cas, IEnumerable entityIds est une liste provenant d’une demande ajax et non d’une requête provenant de quelque part.

Le comportement semble étrange car vous ne considérez pas correctement la sémantique de fermeture. Voir les commentaires ci-dessous:

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

Pourquoi assignez-vous à votre paramètre? Pourquoi pas

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

La réponse est de ne pas affecter la requête LINQ à entityIds . Voir la réponse de @ Stu pour une solution.

Bien sûr, il y a un cycle. Vous utilisez entityIds dans la méthode Where Linq Extension et la requête est elle-même construite. Au lieu de modifier le IEnumerable entré, retournez une nouvelle requête comme suit:

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