El escenario es el siguiente:
Generar predicados linq dinámicos de acuerdo a un conjunto de parámetros variables.
Ejemplo:
Si tengo un listado de elementos, por ejemplo, los libros disponibles en una biblioteca y quiero hacer el filtrado de la misma usando uno o más parámetros de búsqueda. Se puede resolver usando predicados linq dinámicos.
Supongamos que nuestra base de datos de libros está formada por una tabla llamada Libros con los siguientes campos:
El tipo de búsquedas que me gustaría realizar en el listado serían las siguientes:
Ahora veamos un video en el que se implementará dicha solución usando LinqKit
Clase ExpressionExtensions
Esta clase nos permitirá generar la unión de expresiones linq del tipo: AND y OR
Formulario de Prueba
btnBuscar_Click
Este evento validará los criterios ingresados por el usuario, para posteriormente filtrar el listado.
FiltrarListado
Este método genera los predicados linq, los une utilizando ExpressionExtenssions y los aplica al listado general, usando para ello el método AsExpandable(), el cuál está definido como parte de LinqKit, aplicando el predicado final.
Código Fuente
Recuerda que puedes descargar el código fuente usado en el ejemplo del video para que puedas llevar a cabo las pruebas y el entendimiento del código de mejor manera,
Si tienes dudas o comentarios, déjanos un comentario.
Generar predicados linq dinámicos de acuerdo a un conjunto de parámetros variables.
Ejemplo:
Si tengo un listado de elementos, por ejemplo, los libros disponibles en una biblioteca y quiero hacer el filtrado de la misma usando uno o más parámetros de búsqueda. Se puede resolver usando predicados linq dinámicos.
Supongamos que nuestra base de datos de libros está formada por una tabla llamada Libros con los siguientes campos:
- ISBN o identificado único
- Título del libro
- Precio
El tipo de búsquedas que me gustaría realizar en el listado serían las siguientes:
- El título contenga alguna frase ingresada por el usuario
- Que el precio sea mayor al especificado por el usuario
- Que el precio sea menor al especificado por el usuario
- Que el precio se encuentre entre un rango mínimo y máximo
Ahora veamos un video en el que se implementará dicha solución usando LinqKit
Clase ExpressionExtensions
Esta clase nos permitirá generar la unión de expresiones linq del tipo: AND y OR
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | /// <summary> /// Clase usada para la generación de expresiones lambda /// </summary> public static class ExpressionExtensions { #region Métodos /// <summary> /// Método para unión de predicados usando el operador Or /// </summary> /// <typeparam name="T">Tipo al que aplica la expresión</typeparam> /// <param name="expr1">Expresión 1</param> /// <param name="expr2">Expresión 2</param> /// <returns>Predicado del tipo: Expresión 1 OR Expresión 2</returns> public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2) { var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>()); return Expression.Lambda<Func<T, bool>>(Expression.Or(expr1.Body, invokedExpr), expr1.Parameters); } /// <summary> /// Método para unión de predicados usando el operador AND /// </summary> /// <typeparam name="T">Tipo al que aplica la expresión</typeparam> /// <param name="expr1">Expresión 1</param> /// <param name="expr2">Expresión 2</param> /// <returns>Predicado del tipo: Expresión 1 AND Expresión 2</returns> public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2) { var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>()); return Expression.Lambda<Func<T, bool>>(Expression.And(expr1.Body, invokedExpr), expr1.Parameters); } /// <summary> /// Método para unión de predicados usando el operador OR /// </summary> /// <typeparam name="T">Tipo al que aplica la expresión</typeparam> /// <param name="exprs">Conjunto de expresiones a unir</param> /// <returns>Predicado del tipo: Expresión 1 OR Expresión 2 OR ... Expresión N</returns> public static Expression<Func<T, bool>> WhereOr<T> (this IEnumerable<Expression<Func<T, bool>>> exprs) { Expression<Func<T, bool>> FinalQuery = t => false; foreach (var expr in exprs) { FinalQuery = FinalQuery.Or(expr); } return FinalQuery; } /// <summary> /// Método para unión de predicados usando el operador AND /// </summary> /// <typeparam name="T">Tipo al que aplica la expresión</typeparam> /// <param name="exprs">Conjunto de expresiones a unir</param> /// <returns>Predicado del tipo: Expresión 1 AND Expresión 2 AND ... Expresión N</returns> public static Expression<Func<T, bool>> WhereAnd<T> (this IEnumerable<Expression<Func<T, bool>>> exprs) { Expression<Func<T, bool>> FinalQuery = t => true; foreach (var expr in exprs) { FinalQuery = FinalQuery.And(expr); } return FinalQuery; } #endregion } |
Formulario de Prueba
btnBuscar_Click
Este evento validará los criterios ingresados por el usuario, para posteriormente filtrar el listado.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /// <summary> /// Realiza el filtrado del listado de acuerdo a los parámetros ingresados por el usuario /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnBuscar_Click(object sender, EventArgs e) { // Valida que se haya ingresado al menos un parámetro de búsqueda if (tbxNombre.Text.Trim().Length > 0 || tbxPrecioMínimo.Text.Trim().Length > 0 || tbxPrecioMáximo.Text.Trim().Length > 0) { // Valida que los precios estén correctamente formateados if (ValidarPrecio(tbxPrecioMínimo) && ValidarPrecio(tbxPrecioMáximo)) { decimal precioMínimo; decimal.TryParse(tbxPrecioMínimo.Text, NumberStyles.Currency, CultureInfo.CurrentCulture, out precioMínimo); decimal precioMáximo; decimal.TryParse(tbxPrecioMáximo.Text, NumberStyles.Currency, CultureInfo.CurrentCulture, out precioMáximo); // Ejecuta el filtrado del listado FiltrarListado(tbxNombre.Text.Trim(), precioMínimo, precioMáximo); } // Muestra un mensaje indicando que los parámetros de búsqueda // no tienen el formato adecuado else { MessageBox.Show("Revise los criterios de búsqueda proporcionados", "Búsqueda de Libros", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } } |
FiltrarListado
Este método genera los predicados linq, los une utilizando ExpressionExtenssions y los aplica al listado general, usando para ello el método AsExpandable(), el cuál está definido como parte de LinqKit, aplicando el predicado final.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | /// <summary> /// Filtra el listado de acuerdo a los parámetros recibidos /// </summary> /// <param name="título">Título del libro</param> /// <param name="precioMínimo">Precio mínimo</param> /// <param name="precioMáximo">Precio máximo</param> private void FiltrarListado(string título, decimal precioMínimo, decimal precioMáximo) { // Expresión base ISBN no es nulo ni cadena vacia Expression<Func<Libro, bool>> expresiónFinal = l => !string.IsNullOrEmpty(l.ISBN); // Búsqueda por título del libro de tipo Contains if (!string.IsNullOrEmpty(título)) { Expression<Func<Libro, bool>> expresión = l => l.Título.ToUpper().Contains(título.ToUpper()); expresiónFinal = ExpressionExtensions.And(expresiónFinal, expresión); } // Búsqueda por precio mínimo if (precioMínimo > 0 && precioMáximo.Equals(0)) { Expression<Func<Libro, bool>> expresión = l => l.Precio >= precioMínimo; expresiónFinal = ExpressionExtensions.And(expresiónFinal, expresión); } // Búsqueda por precio máximo if (precioMínimo.Equals(0) && precioMáximo > 0) { Expression<Func<Libro, bool>> expresión = l => l.Precio <= precioMáximo; expresiónFinal = ExpressionExtensions.And(expresiónFinal, expresión); } // Búsqueda por precio mínimo y máximo if (precioMínimo > 0 && precioMáximo > 0) { if (precioMínimo <= precioMáximo) { Expression<Func<Libro, bool>> expresión = l => l.Precio >= precioMínimo && l.Precio <= precioMáximo; expresiónFinal = ExpressionExtensions.And(expresiónFinal, expresión); } } // Aplicación del predicado final dgvLibros.DataSource = datos.Elements("bookstore").Elements().Select(b => new Libro() { ISBN = b.Attribute("ISBN").Value, Título = b.Attribute("title").Value, Precio = decimal.Parse(b.Attribute("price").Value) }).AsQueryable().AsExpandable().Where(expresiónFinal).ToList(); } |
Código Fuente
Recuerda que puedes descargar el código fuente usado en el ejemplo del video para que puedas llevar a cabo las pruebas y el entendimiento del código de mejor manera,
Si tienes dudas o comentarios, déjanos un comentario.
Comentarios
Publicar un comentario