Un método de extensión genicial para filtrar las consultas LINQ-EF -- # campo con linq campo con entity-framework campo con generics campo con extension-methods camp codereview Relacionados El problema

A genric extension method to filter Linq-EF queries


4
vote

problema

Español

Tengo varios tipos de entidades EF, todas tienen una propiedad de navegación llamada "Empleado". Al generar informes, el usuario tendrá la opción de filtrar el informe de acuerdo con las diferentes propiedades del empleado (centro de costos, género, etc.).

algo como esto:

  var courses = context.Courses               .Where(c => c.Employee.CostCenterID == ccID                      && c.Employee.Rank == rankID                      ....                     )                     .ToList();   

El código de filtro real es mucho más largo, pero esto fue solo una pista. De todos modos, me di cuenta de una forma más fácil de hacer este filtrado con un método de extensión usando una interfaz:

  public interface IFilterable {     Employee Employee     {         get;         set;     } }   

Luego, agregué clases parciales para heredar la interfaz anterior para diferentes entidades que tiene la propiedad 99887776655544332 , por ejemplo:

  public partial class Course: IFilterable { }   

luego creó el siguiente método genérico:

  public static IQueryable<T> Filter<T>(this IQueryable<T> source, SearchCriteria sc)      where T : class, IFilterable {     var filtered = source.Where(e => e.Employee.CostCenterID == sc.CostCenterID          && e.Employee.Gender == sc.Gender         .....         );       return filtered; }   

Entonces, simplemente puedo usarlo así en cualquier clase que hereda IFilterable :

  var list = context.Courses.Filter(sc).ToList();   

NOTA: El SearchCriteria es solo una clase simple que contiene diferentes propiedades de los empleados.

q: ¿Es esta la forma de hacerlo? ¿O hay una manera más elegante?

Original en ingles

I have various types of EF entities, all of them have a navigation property called "Employee". When generating reports the user will have the option to filter the report according to the different employee properties (Cost Center, gender, etc.).

Something like this:

var courses = context.Courses               .Where(c => c.Employee.CostCenterID == ccID                      && c.Employee.Rank == rankID                      ....                     )                     .ToList(); 

The real filter code is much more longer but this was just a hint. Anyway, I figured out an easier way of doing this filtering with an extension method using an interface:

public interface IFilterable {     Employee Employee     {         get;         set;     } } 

Then, I added partial classes to inherit the previous interface for different entities that has the Employee navigation property, for example:

public partial class Course: IFilterable { } 

Then created the following generic method:

public static IQueryable<T> Filter<T>(this IQueryable<T> source, SearchCriteria sc)      where T : class, IFilterable {     var filtered = source.Where(e => e.Employee.CostCenterID == sc.CostCenterID          && e.Employee.Gender == sc.Gender         .....         );       return filtered; } 

then simply I can use it like this on any class that inherits IFilterable:

var list = context.Courses.Filter(sc).ToList(); 

Note: the SearchCriteria is just a simple class that holds different employee properties.

Q: Is this the way to do it? or is there a more elegant way?

              

Lista de respuestas

1
 
vote

Creo que podría convertirte en alguna fealdad de diseño con el en el futuro porque ahora está acoplando el conocimiento de que una entidad podría ser filtrada por una determinada propiedad a cada entidad.

Imagine un requisito que viene en filtrar por Course - ¿Cómo se vería en su diseño?

¿Va a agregar una propiedad Course a su IFilterable -, entonces, ¿qué pasa si una entidad que implementa actualmente IFilterable no tiene un Course ? Entonces, es posible que deba agregar otra interfaz 9988776665544335 como un método ICourseFilterable 9988777665544337 para ir con él. Y luego, si desea filtrar en otra entidad ... parece bastante engorroso e involucrado para mí.

Creo que la forma más simple y flexible hacia adelante es mover la responsabilidad correspondiente al SearchCriteria . Por lo tanto, en lugar de tener un 9988777665544339 lo renombra a Course0 y déle un método para coincidir con un empleado en particular:

  Course1  

Luego, puede hacer su filtrado a través de:

  Course2  

y en el futuro Si desea filtrar entidades por Course3 Usted crea un Course4 con un método 99887766555443315 que luego puede usar como

  Course6  

La ventaja es que el filtrado por entidades específicas se encapsula en clases individuales con responsabilidades individuales y no tiene que literar su código con todas estas interfaces de filtro.

La preocupación de que una entidad se puede filtrar ahora se elimina de la entidad y se mantiene separada, lo cual siempre es algo bueno. Cuanto más una entidad tenga que saber qué le puede pasar, más complejo se convertirá en su estructura de código.

 

I think you might run into some design ugliness with the in the future because you are now coupling the knowledge that an entity could be filtered by a certain property to each entity.

Imagine a requirement comes in to filter by Course - what would that look like in your design?

Are you going to add a Course property to your IFilterable - but then what if an entity currently implementing IFilterable doesn't have a Course? So you then might need to add another IFilterable interface like an ICourseFilterable and another Filter method to go with it. And then if you want to filter on another entity ... seems quite cumbersome and involved to me.

I think the simplest and most flexible way forward is to actually move the matching responsibility into the SearchCriteria. So instead of having a SearchCriteria you rename it to EmployeeSearchCriteria and give it a method to match a particular employee:

public class EmployeeSearchCriteria {     private int _CostCenterId;     private Gender _Gender;     ... // other fields to match      public EmployeeSearchCriteria(int costCenterId, Gender gender, ...)     {         _CostCenterId = costCenterId;         _Gender = gender;         ...     }      public bool Matches(Employee e)     {         return e.CostCenterID == _CostCenterId && e.Gender == _Gender && ... ;     } } 

Then you can do your filtering via:

var list = context.Courses.Where(c => sc.Matches(c.Employee)).ToList(); 

And in the future if you want to filter entities by Course you create a CourseSearchCriteria with a method bool Matches(Course c) which you then can use as

var list = context.Employees.Where(e => sc.Matches(e.Course)).ToList(); 

The advantage is that the filtering by specific entities is encapsulated into single classes with single responsibilities and you do not have to litter your code with all these filter interfaces.

The concern that an entity can be filtered is now removed from the entity and kept separate which is always a good thing. The more an entity has to know what might happen to it the more complex your code structure will become.

 
 
   
   

Relacionados problema

8  Capitalizar palabras en oración  ( Capitalize words in sentence ) 
public static class StringHelpers { public static string CapitalizeEachWord(this string sentence) { CultureInfo cultureInfo = Thread.CurrentT...

2  Formateo de un precio en SWIFT usando una extensión vs. una clase  ( Formatting a price in swift using an extension vs a class ) 
Tengo un precio como un Double , que debería formatearlo como un String . ¿Debo usar una extensión en lugar de clase clásica para encapsular "Formater", "...

10  Ciclo a través de un iEnumerable  ( Cycle through an ienumerable ) 
He construido un método de extensión para recorrer todos los elementos de un IEnumerable comenzando en algún índice: public static IEnumerable<T> Circle<...

4  Extensiones primitivas: reemplaza los métodos primitivos estáticos  ( Primitive extensions replaces static primitive methods ) 
Me molestó tener que hacer cosas como string.IsNullOrEmpty(myString) Cuando parecía que 9988776665544337 es suficiente. Por lo tanto, escribí una pequeña ...

7  Métodos de extensión, ¿es esto demasiado sucio?  ( Extension methods is this too dirty ) 
Bloqueado . Esta pregunta y sus respuestas son bloqueadas porque la pregunta es off-topic pero tiene importancia histórica. Actualmente no está a...

6  Método LINQ que selecciona un elemento basado en uno anterior  ( Linq method that selects an item based on previous one ) 
Mi requisito es implementar una forma de filtrar una colección de elementos basados ​​en un valor de uno anterior. Por ejemplo, solo para seleccionar un númer...

7  JavaScript Herencia: ¿Es correcta mi solución?  ( Javascript inheritance is my solution correct ) 
He estado desarrollando JavaScript durante unos meses, y, como ex desarrollador de Java, necesitaba una forma sencilla de realizar la herencia de la clase. ...

10  Pequeña extensión de BOOST C ++ basada en Boost :: Property_Tree  ( Small c boost extension based on boostproperty tree ) 
Esta es una pequeña extensión basada en un boost::property_tree , que admite valores arbitrarios para las propiedades y serialización adecuada. Puede usarse ...

10  Encapsulando el código de prueba de prueba común. ¿Es este un patrón conocido? ¿Es bueno o malo?  ( Encapsulating common try catch code is this a known pattern is it good or bad ) 
En un esfuerzo por reducir la duplicación de código, a menudo uso y he usado este estilo para capturar el manejo de excepciones en un límite de una aplicación...

8  Convertir objetos para escribir bool  ( Converting objects to type bool ) 
Se está utilizando el siguiente método de extensión en nuestra base de código: public static bool ToBool(this object src) { return src != null && ((str...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos