2012-09-13 9 views
6

Estoy utilizando ahora Entity framework, pero es un problema "compartido" entre todos los ORM e incluso IEnumerable.Cómo filtrar IEnumerable basado en un parámetro de entrada de entidad

Digamos que tengo un método en el MVC se ve así:

[HttpPost] 
public ActionResult Foo(FooModel model) 
{ 
    var context = new Context(); -- The EF session 
    var data = context.Foo.Where(???).ToList(); 
    return View(data); 
} 

Quiero consultar el contexto basado en el parámetro de entrada como:

var data = context.Foo.Where(x => x.Date == model.Date && 
          x.Name == model.Name && 
          x.ItemCode = model.ItemCode).ToList(); 

pero es más complicado que eso, porque si uno de los parámetros anteriores (Date \ Name \ ItemCode) es nulo, no quiero incluirlo dentro de la consulta.
Si I Código difícil que puede ser similar a ésta:

var query = context.Foo; 

if (model.Date != null) 
    query =query.Where(x => x.Date == model.Date); 

if (model.ItemCode != null) 
    query =query.Where(x => x.ItemCode == model.ItemCode); 
... 

Tiene que haber una manera más simple que esto.
Necesito una forma de generar una expresión del tipo Expression<T, bool> para usar en el método Where.

[HttpPost] 
public ActionResult Foo(FooModel model) 
{ 
    var context = new Context(); -- The EF session 
    var data = context.Foo.Where(THE_EXPRESSION).ToList(); 
    return View(data); 
} 

¿Hay una forma incorporada de construir esa expresión? ¿Hay algún paquete en nuget que lo haga?


Actualización: Podría haber más de 30 properites en el modelo de entidad; escribir 30 veces el lugar para cada consulta puede ser un dolor en el cuello:

.Where(model.Date != null, x => x.Date == model.Date) 
.Where(model.Name != null, x => x.Name == model.Name) 
.Where(model.ItemCode != null, x => x.ItemCode == model.ItemCode) 
... 
... 
... 
.ToList(); 

Respuesta

5

Prueba eso. Esto usa reflexión y expresiones para construir la consulta dinámicamente. Lo probé solo con objetos.

static IQueryable<T> Filter<T>(IQueryable<T> col, T filter) 
{ 
    foreach (var pi in typeof(T).GetProperties()) 
    { 
     if (pi.GetValue(filter) != null) 
     { 
      var param = Expression.Parameter(typeof(T), "t"); 
      var body = Expression.Equal(
       Expression.PropertyOrField(param, pi.Name), 
       Expression.PropertyOrField(Expression.Constant(filter), pi.Name)); 
      var lambda = Expression.Lambda<Func<T, bool>>(body, param); 
      col = col.Where(lambda); 
     } 
    } 

    return col; 
} 
+0

Un ejemplo que muestra cómo se usa este método será muy útil. – devinbost

5

Su método codificado es el mejor método en general.

Sin embargo, puede intentar hacer su vida un poco más fácil al escribir un método de extensión apropiado para ayudar a mantener el código limpio.

probar esto por ejemplo:

public static class QueryableEx 
{ 
    public static IQueryable<T> Where<T>(
     this IQueryable<T> @this, 
     bool condition, 
     Expression<Func<T, bool>> @where) 
    { 
     return condition ? @this.Where(@where) : @this; 
    } 
} 

Ahora usted podría escribir este código:

[HttpPost] 
public ActionResult Foo(FooModel model) 
{ 
    using (var context = new Context()) 
    { 
     var data = context.Foo 
      .Where(model.Date != null, x => x.Date == model.Date) 
      .Where(model.Name != null, x => x.Name == model.Name) 
      .Where(model.ItemCode != null, x => x.ItemCode == model.ItemCode) 
      .ToList(); 
     return View(data); 
    } 
} 

(Por favor, no se olvide de deshacerse de su contexto o usar using que lo haga por usted.)

+0

+1 Esto obtiene mi voto - pantallas que permiten al usuario buscar una tabla grande por N! diferentes permutaciones hacen que sea una pesadilla indexar a nivel de DB. Al hacer que cada filtro sea visible para el desarrollador, por lo menos hay algo de visibilidad en la consulta resultante, y puede causar un replanteamiento en cuanto a qué campos (o combinaciones de los mismos) pueden estar expuestos al usuario. – StuartLC

+0

Gracias por su aporte e idea.Pero si mi modelo de entidad tiene más de ** 30 ** propiedades, escriba 30 veces Donde (...) puede ser un problema. – gdoron

+0

@gdoron - Sí, pero debe escribirlos en alguna parte. Si tratas de hacer esto con reflexión y construyendo expresiones, me temo que ahora tendrás dos problemas. – Enigmativity

1

Creo que debe encapsular su lógica en su entidad Foo, por ejemplo

public Foo 
    { 
    public bool isMatch(Model model) 
    { 
     // check your rules and return result 
    } 
    } 

y úselo en linq. O mira Specification pattern

+1

Eso solo funciona con LINQ to Objects, no con Entity Framework. –

+0

Puede devolver public Expression > IsSatisfiedBy() – syned

+0

Que polutes completamente su entidad. –

Cuestiones relacionadas