Come aggiungere dynamicmente l’operatore OR alla clausola WHERE in LINQ

Ho una matrice di stringhe di dimensioni variabili, e sto provando a scorrere in modo programmato l’array e ad abbinare tutte le righe in una tabella in cui la colonna “Tag” contiene almeno una delle stringhe dell’array. Ecco alcuni pseudo codice:

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

Posso facilmente interrogare questo filtro delle tabelle su un insieme fisso di stringhe, come questo:

  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 dell’istruzione non può essere convertita in un albero di espressioni”)

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

Qualcuno può mostrarmi la strategia corretta per realizzare questo? Sono ancora nuovo nel mondo di LINQ 🙂

Puoi utilizzare la class PredicateBuilder :

 var searchPredicate = PredicateBuilder.False(); 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 comportamento strano

Di recente ho creato un metodo di estensione per la creazione di ricerche di stringhe che consente anche ricerche OR . Bloggato qui

L’ho anche creato come pacchetto nuget che puoi installare:

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

Una volta installato sarai in grado di fare quanto segue

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

Se si desidera creare la propria versione per consentire quanto sopra, sarà necessario effettuare quanto segue:

 public static class QueryableExtensions { public static IQueryable Search(this IQueryable source, Expression> 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>(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, dai un’occhiata al progetto github per NinjaNye.SearchExtensions dato che questo ha altre opzioni ed è stato refactored un po ‘per consentire altre combinazioni

Costruisci Expression o guarda una strada diversa.

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

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

C’è un altro, un metodo un po ‘più semplice che realizzerà questo. Il blog di ScottGu descrive in dettaglio una libreria linq dynamic che ho trovato molto utile in passato. In sostanza, genera la query da una stringa passata. Ecco un esempio del codice che scriverebbe:

 Dim Northwind As New NorthwindDataContext Dim query = Northwind.Products _ .Where("CategoryID=2 AND UnitPrice>3") _ .OrderBy("SupplierId") Gridview1.DataSource = query Gridview1.DataBind() 

Maggiori informazioni possono essere trovate sul blog di scottgu qui.