Linq: agregar condiciones a la cláusula where condicionalmente

103

Tengo una consulta como esta

(from u in DataContext.Users
       where u.Division == strUserDiv 
       && u.Age > 18
       && u.Height > strHeightinFeet  
       select new DTO_UserMaster
       {
         Prop1 = u.Name,
       }).ToList();

Quiero agregar las diversas condiciones como edad, altura en función de si esas condiciones se proporcionaron al método que ejecuta esta consulta. Todas las condiciones incluirán la división de usuarios. Si se proporcionó la edad, quiero agregar eso a la consulta. Del mismo modo, si se proporcionó la altura, también quiero agregar eso.

Si esto se hiciera usando consultas sql, habría usado el generador de cadenas para agregarlas a la consulta strSQL principal. Pero aquí en Linq solo puedo pensar en usar una condición IF en la que escribiré la misma consulta tres veces, con cada bloque IF teniendo una condición adicional. ¿Hay una mejor manera de hacer esto?

user20358
fuente

Respuestas:

182

Si no llama ToList()y su asignación final al tipo DTO, puede agregar Wherecláusulas sobre la marcha y generar los resultados al final:

var query = from u in DataContext.Users
   where u.Division == strUserDiv 
   && u.Age > 18
   && u.Height > strHeightinFeet
   select u;

if (useAge)
   query = query.Where(u => u.Age > age);

if (useHeight)
   query = query.Where(u => u.Height > strHeightinFeet);

// Build the results at the end
var results = query.Select(u => new DTO_UserMaster
   {
     Prop1 = u.Name,
   }).ToList();

Esto aún resultará en una única llamada a la base de datos, que será efectivamente tan eficiente como escribir la consulta en una pasada.

Reed Copsey
fuente
1
¿Necesito poner todas las condiciones where en la instrucción "var query = .."?
user20358
4
Las siguientes condiciones Where se agregan como OR o AND?
Vi100
4
@ vi100 serán filtros adicionales, así que Y
Reed Copsey
¡Gracias a Dios por la sencillez! Estoy tan harto de ver más de 20 consultas de Linq de línea cuando lo anterior es mucho más legible
justanotherdev
Por qué recibo este errorLINQ to Entities does not recognize the method 'System.String get_Item(System.String)' method, and this method cannot be translated into a store expression.
Ali Umair
19

Normalmente uso el encadenamiento de métodos pero tengo el mismo problema. Y aquí está la extensión que uso

public static IQueryable<T> ConditionalWhere<T>(
        this IQueryable<T> source, 
        Func<bool> condition,
        Expression<Func<T, bool>> predicate)
    {
        if (condition())
        {
            return source.Where(predicate);
        }

        return source;
    }

Ayuda a evitar roturas de cadena. También lo mismo ConditionalOrderByy ConditionalOrderByDescendingson útiles.

Yuriy Granovskiy
fuente
Útil, pero ¿podría agregar un ejemplo de cómo se vería en uso?
Suncat2000
1
Debería ser como: var frutas = aguardar db.Fruits .ConditionalWhere (() => color! = Null, f => f.Color == color) .ConditionalWhere (() => ripe! = Null, f => f .Maduro == maduro) .ToListAsync ();
Yuriy Granovskiy
4
¡Funciona genial! ¡Gracias! También hice una sobrecarga para la condición como un valor booleano simple en lugar de una función, para hacerlo más intuitivo donde un delegado agregaría complejidad innecesaria. Utilizo este método de extensión con bastante frecuencia ahora y aprecio mucho su solución.
Suncat2000
17

una opción.

bool? age = null

(from u in DataContext.Users
           where u.Division == strUserDiv 
           && (age == null || (age != null && u.Age > age.Value))
           && u.Height > strHeightinFeet  
           select new DTO_UserMaster
           {
             Prop1 = u.Name,
           }).ToList();

o puede cambiar a la sintaxis del método para linq y usar condiciones if para adjuntar expresiones a la cláusula where.

Matthew Vines
fuente
3

Simplemente lo estoy usando en mi cláusula where como

    public IList<ent_para> getList(ent_para para){
     db.table1.Where(w=>(para.abc!=""?w.para==para.abc:true==true) && (para.xyz!=""?w.xyz==para.xyz:true==true)).ToList();
}
Propio
fuente
3

Según ciertas condiciones, agregue la condición where ...

from u in DataContext.Users
where u.Division == strUserDiv 
&& u.Age != null ? u.Age > 18 : 1== 1
&& u.Height != null ? u.Height > 18 : 1== 1
&& u.Height != null ? u.Height > 18 : 1== 1
 select new DTO_UserMaster
       {
         Prop1 = u.Name,
       }).ToList();
Melu
fuente
2

Aquí está mi código para hacer algo similar. Este es un método en mi API WCF SOAP Web Service.

    public FruitListResponse GetFruits(string color, bool? ripe)
    {
        try
        {
            FruitContext db = new FruitContext();
            var query = db.Fruits.Select(f => f);
            if (color != null)
            {
                query = query.Where(f => f.Color == color);
            }
            if (ripe != null)
            {
                query = query.Where(f => f.Ripe == ripe);
            }
            return new FruitListResponse
            {
                Result = query.Select(f => new Fruit { Id = f.FruitId, Name = f.Name }).ToList()
            };
        }
        catch (Exception e)
        {
            return new FruitListResponse { ErrorMessage = e.Message };
        }
    }

La consulta base es lo Select(f => f)que significa básicamente TODO, y las Wherecláusulas se adjuntan opcionalmente. La final Selectes opcional. Utilizo para convertir los objetos de las filas de la base de datos en objetos de resultado "Fruta".

John Henckel
fuente
0

Suponiendo el siguiente parámetro,

Int? Age = 18;

Simplemente usando &&y ||operadores condicionales podemos tener otra versión.

(from u in DataContext.Users
where u.Division == strUserDiv 
    && (Age == null || u.Age > Age)
    && (Param1 == null || u.param1 == Param1)
    && u.Height > strHeightinFeet
select new DTO_UserMaster
{
    Prop1 = u.Name,
}).ToList();

Al igual que Param1, puede agregar cualquier número de parámetros para la condición de búsqueda.

Sushant Yelpale
fuente
0

Me encontré con esto buscando algo más, pero pensé en lanzar la versión lambda.

Primero, crearía una clase como esta para pasar parámetros a una capa de datos:

   public class SearchParameters() {
       public int? Age {get; set;}
       public string Division {get;set;}
       etc
    }

Luego, en mi capa de datos, algo como esto:

public IQueryable<User> SearchUsers(SearchParameters params) 
{
    var query = Context.Users;
    if (params.Age.HasValue)
    {
         query = query.Where(u => u.Age == params.Age.Value);
    }
    if (!string.IsNullOrEmpty(params.Division)
    {
        query = query.Where(u => u.Division == params.Division);
    }
    etc
    return query;
}

Depende de usted dónde materialice la consulta. Puede tener una capa entre la aplicación y los datos que convierta las representaciones específicas de la base de datos en agnósticas de la base de datos (tal vez consulte varias fuentes de datos). Esa capa puede obtener varios tipos de consultables de estas fuentes y asignarlos a una representación POCO común, por ejemplo.

Scott Peterson
fuente
Vaya, no vi la respuesta de John Henckel. La misma idea.
Scott Peterson
0

Solo para agregar a la respuesta aceptada anterior aquí , si está haciendo una búsqueda dinámica en una combinación, considere devolver un nuevo objeto con ambas tablas (t1, t2) en la consulta inicial de linq para que pueda acceder a ellos individualmente para hacer el condicional buscar.

var query = from t1 in _context.Table1
            join t2 in _context.Table2 on t1.Table1Id equals t2.Table1IdId
            select new { t1, t2 };

        if (!string.IsNullOrEmpty(searchProperty1))
        {
            query = query.Where(collection => collection.t1.TableColumn == searchProperty1);
        }
        if (!string.IsNullOrEmpty(searchProperty2))
        {
            query = query.Where(collection => collection.t2.TableColumn == searchProperty2);
        }
        ....etc.

Obtuve la respuesta que estaba buscando aquí con respecto a unir dos tablas y consultar columnas específicas en cualquiera de las tablas

Riaan Saayman
fuente