2010-05-30 8 views
5

Ich verwende Code (verfügbar here auf MSDN), um LINQ-Ausdrücke dynamisch zu erstellen, die mehrere OR-Klauseln enthalten.Erstellen von "flachen" LINQ-Ausdrücken

Der entsprechende Code ist

var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue)))); 

var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal)); 

Dies erzeugt einen LINQ Ausdruck, der etwa wie folgt aussieht:

(((((ID = 5) OR (ID = 4)) OR (ID = 3)) OR (ID = 2)) OR (ID = 1)) 

Ich schlage die Rekursion Grenze (100), wenn man diesen Ausdruck verwendet, so dass ich Möchte einen Ausdruck erzeugen, der so aussieht:

(ID = 5) OR (ID = 4) OR (ID = 3) OR (ID = 2) OR (ID = 1) 

Wie würde ich die ex modifizieren Press Building Code, um dies zu tun?

Antwort

6

Sie benötigen die Erzeugung so zu modifizieren, dass Es erstellt einen Balanced-Baum anstelle einer Sequenz von OR s, wobei der linke Teilbaum ein einzelner Ausdruck und der rechte Teilbaum alle verbleibenden Elemente enthält. Grafisch:

Your code    Better 
---------    -------- 
    OR      OR 
#1 OR    OR  OR 
    #2 OR   #1 #2 #3 #4 
     #3 #4 

Wie Sie sehen können, auch in diesem einfachen Fall der bessere Ansatz ist nicht so tief (rekursiv verschachtelte). Der Code den besseren Ausdrucksbaum zu erzeugen, kann als eine rekursive Methode in C# geschrieben werden:

Expression GenerateTree(List<Expression> exprs, int start, int end) { 
    // End of the recursive processing - return single element 
    if (start == end) return exprs[start]; 

    // Split the list between two parts of (roughly the same size) 
    var mid = start + (end - start)/2; 
    // Process the two parts recursively and join them using OR 
    var left = GenerateTree(exprs, start, mid); 
    var right = GenerateTree(exprs, mid+1, end); 
    return Expression.Or(left, right); 
} 

// Then call it like this: 
var equalsList = equals.ToList(); 
var body = GenerateTree(equalsList, 0, equalsList.Length); 

Ich habe nicht versucht, den Code, so kann es einige kleine Fehler, aber es sollte die Idee zeigen.

+0

Winzige Änderung - ersetze equalsList.Length mit equalsList.Count-1 - und es funktioniert perfekt. Vielen Dank. –

1

Wenn dies gemäß Ihren Tags wirklich LINQ to Objects ist, warum erstellen Sie überhaupt Ausdrucksbäume? Sie können Delegaten sehr einfach verwenden und haben kein Rekursionslimit.

jedoch mehr auf den Punkt: Wenn Sie nur sehen wollen, ob eine ID in einer bestimmten Sammlung ist, warum Sie nicht mit so etwas wie:

var query = from item in source 
      where idCollection.Contains(item.Id) 
      ... 
+0

Entschuldigung, meine Markierung war falsch. Ich verwende WCF Data Services, wo die Rekursionsgrenze auftritt. –

+0

@Ian: Kann WCF Data Services Contains nicht verwenden? Das wäre immer noch der bevorzugte Ansatz IMO ... –

+0

Es ist nicht in .NET 3.5. Es kann Contains nicht in URI-Syntax konvertieren. –

Verwandte Themen