2009-04-23 2 views
27

Ho una matrice di stringhe di dimensioni variabili e sto provando a scorrere in modo programmato l'array e ad associare tutte le righe in una tabella in cui la colonna "Tag" contiene almeno una delle stringhe dell'array. Ecco alcuni pseudo codice:Come aggiungere dinamicamente l'operatore O alla clausola WHERE in LINQ

IQueryable<Songs> allSongMatches = musicDb.Songs; // all rows in the table 

posso facilmente interrogare questo filtraggio tavolo su un insieme fisso di stringhe, in questo modo:

allSongMatches=allSongMatches.Where(SongsVar => SongsVar.Tags.Contains("foo1") || SongsVar.Tags.Contains("foo2") || SongsVar.Tags.Contains("foo3")); 

Tuttavia, questo non funziona (ottengo il seguente errore: "un'espressione lambda con un corpo dichiarazione non può essere convertito in un albero di espressione")

allSongMatches = allSongMatches.Where(SongsVar => 
    { 
     bool retVal = false; 
     foreach(string str in strArray) 
     { 
     retVal = retVal || SongsVar.Tags.Contains(str); 
     } 
     return retVal; 
    }); 

qualcuno mi può mostrare la strategia corretta per raggiungere questo obiettivo? Sono ancora nuovo al mondo di LINQ :-)

risposta

32

È possibile utilizzare la classe PredicateBuilder:

var searchPredicate = PredicateBuilder.False<Songs>(); 

foreach(string str in strArray) 
{ 
    var closureVariable = str; // See the link below for the reason 
    searchPredicate = 
    searchPredicate.Or(SongsVar => SongsVar.Tags.Contains(closureVariable)); 
} 

var allSongMatches = db.Songs.Where(searchPredicate); 

LinqToSql strange behaviour

+0

Grazie Mehrdad, la soluzione è venuto a tempo di record e ha lavorato come un fascino! Non ero a conoscenza della classe PredicateBuilder. Che caratteristica utile! Apprezzo anche il tuo commento sull'uso di una variabile temporanea per tenere le stringhe ... che mi avrebbe fatto impazzire! Victor –

+0

Prego! –

+0

Mi sono confuso circa l'espressione False per creare il var searchPredicate, Questo deve essere falso? o qualcos'altro si adatterebbe? (Parlando di PredicateBuilder.False ()) – Daniel

0

O costruire un Expression<T> te stesso, o guardare un percorso diverso.

Supponendo che Tag è una raccolta di tag, è possibile utilizzare una chiusura e un join per trovare le corrispondenze. Questo dovrebbe trovare qualsiasi canzone con almeno un tag in possibili Tag:

allSongMatches = allSongMatches.Where(s => (select t from s.Tags 
              join tt from possibleTags 
               on t == tt 
              select t).Count() > 0) 
+0

Hey Richard, apprezzo il tuo feedback . Per qualche ragione, a VS non piaceva la sintassi del codice che hai proposto ... mi manca qualcosa di ovvio? –

+0

Molto probabilmente ha un errore ... avrebbe bisogno di un po 'di tempo per mettere insieme i dati/il cablaggio di test per confermare. Ma ho aggiunto una clausola selezionata all'espressione di comprensione interiore ... che è richiesta (oops). – Richard

0

C'è un altro, un po 'più semplice metodo che lo compirà. Il blog di ScottGu descrive in dettaglio una libreria linq dinamica che ho trovato molto utile in passato. In sostanza, esso genera la query da una stringa si passa Ecco un esempio del codice che ci scrive:.

Dim Northwind As New NorthwindDataContext 

Dim query = Northwind.Products _ 
        .Where("CategoryID=2 AND UnitPrice>3") _ 
        .OrderBy("SupplierId") 

Gridview1.DataSource = query 
Gridview1.DataBind() 

Maggiori informazioni sono disponibili all'indirizzo blog di ScottGu here.

+0

non so perché questo -1, sembra una risposta legittima – piris

1

recente ho creato un metodo di estensione per creare ricerche di stringhe che consentano anche ricerche OR. Bloggato su here

ho anche creato come un pacchetto di NuGet che è possibile installare:

http://www.nuget.org/packages/NinjaNye.SearchExtensions/

una volta installato si sarà in grado di effettuare le seguenti operazioni

var result = db.Songs.Search(s => s.Tags, strArray); 

Se vuoi creare la tua versione per consentire quanto sopra, dovrai fare quanto segue:

public static class QueryableExtensions 
{ 
    public static IQueryable<T> Search<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, params string[] searchTerms) 
    { 
     if (!searchTerms.Any()) 
     { 
      return source; 
     } 

     Expression orExpression = null; 
     foreach (var searchTerm in searchTerms) 
     { 
      //Create expression to represent x.[property].Contains(searchTerm) 
      var searchTermExpression = Expression.Constant(searchTerm); 
      var containsExpression = BuildContainsExpression(stringProperty, searchTermExpression); 

      orExpression = BuildOrExpression(orExpression, containsExpression); 
     } 

     var completeExpression = Expression.Lambda<Func<T, bool>>(orExpression, stringProperty.Parameters); 
     return source.Where(completeExpression); 
    } 

    private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd) 
    { 
     if (existingExpression == null) 
     { 
      return expressionToAdd; 
     } 

     //Build 'OR' expression for each property 
     return Expression.OrElse(existingExpression, expressionToAdd); 
    } 
} 

In alternativa, dare un'occhiata al progetto github per NinjaNye.SearchExtensions come questo ha altre opzioni ed è stata riscritta in qualche modo per consentire altre combinazioni