C #: Ein Element mit demselben Schlüssel wurde bereits beim Kompilieren des Ausdrucks hinzugefügt

c# exception expression expression-trees lambda

Frage

Ok, hier ist ein heikles Thema. Hoffentlich gibt es hier einen Ausdrucksguru, der erkennen kann, was ich hier falsch mache, weil ich es einfach nicht verstehe.

Ich entwickle Ausdrücke, die ich zum Filtern von Abfragen verwende. Um diesen Prozess zu erleichtern, habe ich einige Expression<Func<T, bool>> -Erweiterungsmethoden, die meinen Code sauberer machen und bis jetzt haben sie gut funktioniert. Ich habe Tests für alle geschrieben mit Ausnahme einer, für die ich heute eine geschrieben habe. Und dieser Test schlägt vollständig mit einer ArgumentException mit einer langen Stack-Ablaufverfolgung fehl. Und ich verstehe es einfach nicht. Zumal ich diese Methode seit einiger Zeit erfolgreich in meinen Anfragen verwende!

Wie auch immer, hier ist der Stack-Trace, den ich beim Ausführen des Tests bekomme:

failed: System.ArgumentException : An item with the same key has already been added.
    at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
    at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
    at System.Linq.Expressions.ExpressionCompiler.PrepareInitLocal(ILGenerator gen, ParameterExpression p)
    at System.Linq.Expressions.ExpressionCompiler.GenerateInvoke(ILGenerator gen, InvocationExpression invoke, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedAndAlso(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateAndAlso(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedOrElse(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateOrElse(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateInvoke(ILGenerator gen, InvocationExpression invoke, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedAndAlso(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateAndAlso(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateLambda(LambdaExpression lambda)
    at System.Linq.Expressions.ExpressionCompiler.CompileDynamicLambda(LambdaExpression lambda)
    at System.Linq.Expressions.Expression`1.Compile()
    PredicateTests.cs(257,0): at Namespace.ExpressionExtensionsTests.WhereWithin_CollectionIsFilteredAsExpected()

Der Test selbst sieht wie folgt aus, und es schlägt bei der Compile-Anweisung fehl:

failed: System.ArgumentException : An item with the same key has already been added.
    at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource)
    at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)
    at System.Linq.Expressions.ExpressionCompiler.PrepareInitLocal(ILGenerator gen, ParameterExpression p)
    at System.Linq.Expressions.ExpressionCompiler.GenerateInvoke(ILGenerator gen, InvocationExpression invoke, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedAndAlso(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateAndAlso(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedOrElse(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateOrElse(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateInvoke(ILGenerator gen, InvocationExpression invoke, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateUnliftedAndAlso(ILGenerator gen, BinaryExpression b)
    at System.Linq.Expressions.ExpressionCompiler.GenerateAndAlso(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateBinary(ILGenerator gen, BinaryExpression b, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.Generate(ILGenerator gen, Expression node, StackType ask)
    at System.Linq.Expressions.ExpressionCompiler.GenerateLambda(LambdaExpression lambda)
    at System.Linq.Expressions.ExpressionCompiler.CompileDynamicLambda(LambdaExpression lambda)
    at System.Linq.Expressions.Expression`1.Compile()
    PredicateTests.cs(257,0): at Namespace.ExpressionExtensionsTests.WhereWithin_CollectionIsFilteredAsExpected()

Ich verstehe die Fehlermeldung einfach nicht. Ich dachte, es könnte damit zu tun haben, dass ich immer x als Parameternamen benutze, aber es schien nicht zu helfen, als ich versuchte, sie zu tauschen. Was es noch seltsamer für mich macht, ist, dass ich diese genaue Methode schon länger in größeren Linq2Sql-Abfragen verwendet habe und sie immer gut funktioniert haben. In meinem Test habe ich also versucht, den Ausdruck nicht zu kompilieren und AsQueryable damit ich ihn stattdessen verwenden kann. Aber das hat nur dazu geführt, dass die Ausnahme im ToArray . Was geht hier vor sich? Wie kann ich das beheben?

Sie finden den lästigen und lästigen Code in der Zip-Datei unterhalb der Zeile:


Hinweis: Ich hatte hier einen Teil des zugehörigen Codes gepostet, aber nach einigen Kommentaren beschloss ich, den Code in sein eigenes Projekt zu extrahieren, das die Ausnahme deutlicher zeigt. Und noch wichtiger, das kann ausgeführt, kompiliert und debuggt werden.


Update: Vereinfacht das Beispielprojekt mit einigen Vorschlägen von @Mark. Wie Entfernen der Bereichsklasse und stattdessen nur hartes Kodieren einzelner konstanter Bereiche. Es wurde auch ein anderes Beispiel hinzugefügt, bei dem die genau gleiche Methode tatsächlich funktioniert. Die Verwendung der AndWithin-Methode führt zum Absturz der App, während die WhereWithin-Methode problemlos funktioniert. Ich fühle mich ziemlich ahnungslos!

Beliebte Antwort

Ich habe Ihre Methoden ein wenig überarbeitet, um den Compiler etwas glücklicher zu machen:

public static Expression<Func<TSubject, bool>> AndWithin<TSubject, TField>(
    this Expression<Func<TSubject, bool>> original,
    IEnumerable<Range<TField>> range, Expression<Func<TSubject, TField>> field) where TField : IComparable<TField>
{
  return original.And(range.GetPredicateFor(field));
}


static Expression<Func<TSource, bool>> GetPredicateFor<TSource, TValue>
    (this IEnumerable<Range<TValue>> range, Expression<Func<TSource, TValue>> selector) where TValue : IComparable<TValue>
{
  var param = Expression.Parameter(typeof(TSource), "x");

  if (range == null || !range.Any())
    return Expression.Lambda<Func<TSource, bool>>(Expression.Constant(false), param);

  Expression body = null;
  foreach (var r in range)
  {
    Expression<Func<TValue, TValue, TValue, bool>> BT = (val, min, max) => val.CompareTo(min) >= 0 && val.CompareTo(max) <= 0;
    var newPart = Expression.Invoke(BT, param,
                                      Expression.Constant(r.Start, typeof(TValue)),
                                      Expression.Constant(r.End, typeof(TValue)));

    body = body == null ? newPart : (Expression)Expression.OrElse(body, newPart);
  }

  return Expression.Lambda<Func<TSource, bool>>(body, param);
}

Beide haben die zusätzliche Einschränkung von IComparable<TValue> (die einzige Änderung der ersten Methode).

In der zweiten, ich mache den Vergleich über eine Func Expression-Implementierung, beachten Sie, dass die Funktion in der Schleife erstellt wird ... es ist die zweite Hinzufügung von dies (was es denkt, ist gleich ...) Ausdruck in der alten Methode das ist in die Luft gesprengt.

Disclaimer: Ich verstehe immer noch nicht ganz, warum Ihre bisherige Methode nicht funktioniert hat, aber dieser alternative Ansatz umgeht das Problem. Lass es mich wissen, wenn du nicht danach suchst und wir versuchen etwas anderes.

Auch ein großes Lob für eine gute Frage, ein Beispielprojekt ist vorbildlich.




Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum