2009-04-23 11 views
27

Tengo una matriz de cadenas de tamaño variable, y estoy tratando de realizar un ciclo programático de la matriz y unir todas las filas en una tabla donde la columna "Etiquetas" contiene al menos una de las cadenas en la matriz. Aquí hay un código de pseudo:Cómo agregar dinámicamente el operador OR a la cláusula WHERE en LINQ

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

puedo consultar fácilmente este filtrado mesa en un conjunto fijo de cuerdas, así:

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

Sin embargo, esto no funciona (me da el siguiente error: "una expresión lambda con un cuerpo de la declaración no puede ser convertido a un árbol de expresión")

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

¿alguien puede mostrar la estrategia correcta para lograr esto? Todavía soy nuevo en el mundo de LINQ :-)

Respuesta

32

Puede utilizar la clase 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

Gracias Mehrdad, la solución llegó en un tiempo récord y funcionó como un encanto! No tenía conocimiento de la clase PredicateBuilder. ¡Qué característica más útil! También aprecio su comentario sobre el uso de una variable temporal para mantener las cuerdas ... ¡eso me hubiera vuelto loco! Victor –

+0

De nada! –

+0

Me confundí sobre la expresión False para crear la búsqueda varPredicate, This need to be false? o cualquier otra cosa cabría? (Hablando del PredicateBuilder.False ()) – Daniel

0

sea para construir un Expression<T> usted mismo, o mirar una ruta diferente.

Asumiendo possibleTags es una colección de etiquetas, puede hacer uso de un cierre y una unión para encontrar coincidencias. Esto debería encontrar cualquier canción con al menos una etiqueta en posibles. Etiquetas:

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

Hola, Richard, aprecio tus comentarios . Por alguna razón, a VS no le gustó la sintaxis del código que propusiste ... ¿Me falta algo obvio? –

+0

Posiblemente tenga un error ... necesitaría un poco de tiempo para armar los datos/arnés de prueba para confirmar. Pero he agregado una cláusula de selección a la expresión de comprensión interna ... que se requiere (¡vaya!). – Richard

0

Hay otro método, algo más fácil, que lo hará. El blog de ScottGu detalla una biblioteca dinámica de linq que he encontrado muy útil en el pasado. En esencia, se genera la consulta de una cadena que se pasa en He aquí un ejemplo del código que iba a escribir:.

Dim Northwind As New NorthwindDataContext 

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

Gridview1.DataSource = query 
Gridview1.DataBind() 

Más información se puede encontrar en el blog de ScottGu here.

+0

no estoy seguro de por qué este -1, parece una respuesta legítima – piris

1

recientemente he creado un método de extensión para crear búsquedas de cadenas que también permite búsquedas de OR. Blogs sobre here

También creé como un paquete Nuget que se puede instalar:

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

Una vez instalado, usted será capaz de hacer lo siguiente

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

Si desea crear su propia versión para permitir lo anterior, deberá hacer lo siguiente:

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

opción es tomar un vistazo al proyecto github para NinjaNye.SearchExtensions ya que tiene otras opciones y se ha rediseñado un poco para permitir que otras combinaciones

Cuestiones relacionadas