2010-05-30 13 views
5

Sto utilizzando del codice (disponibile here su MSDN) per creare dinamicamente espressioni LINQ contenenti più "clausole" OR.Creazione di espressioni "piatte" anziché "albero" LINQ

Il codice di riferimento è

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

Questo genera un'espressione LINQ che sembra qualcosa di simile:

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

sto colpendo il limite di ricorsione (100) quando si usa questa espressione, in modo da Mi piacerebbe generare un'espressione simile a questa:

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

Come modificare l'ex codice di costruzione della pression per fare questo?

risposta

6

È necessario modificare la generazione in modo che costruisce un albero bilanciato invece di una sequenza di OR s in cui il sottoalbero sinistro è una singola espressione e il sottoalbero destro contiene tutti gli elementi rimanenti. Graficamente:

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

Come si può vedere, anche in questo caso semplice, l'approccio migliore non è così profondamente (in modo ricorsivo nidificato). Il codice per generare l'albero di espressione migliore può essere scritta come un metodo ricorsivo in C#:

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

non ho provato il codice, quindi non ci possono essere alcuni piccoli errori, ma dovrebbe mostrare l'idea.

+0

Piccolo cambiamento - sostituire equalsList.Length con equalsList.Count-1 - e funziona perfettamente. Grazie. –

1

Se questo è veramente LINQ per gli oggetti secondo i tag, perché stai costruendo alberi di espressione? Puoi usare i delegati molto facilmente e non avranno un limite di ricorsione.

Tuttavia, più al punto: se si desidera solo per vedere se un ID è in qualche collezione particolare, perché non stai usando qualcosa come:

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

Mi scuso, il mio tag non è corretto. Sto usando WCF Data Services, che è il punto in cui viene rilevato il limite di ricorsione. –

+0

@Ian: WCF Data Services non consente di utilizzare Contiene? Questo sarebbe comunque l'approccio preferito IMO ... –

+0

Non in .NET 3.5. Non può convertire Contains in sintassi URI. –

Problemi correlati