11

Estoy tratando de decidir sobre el mejor patrón para el acceso a los datos en mi aplicación MVC. Actualmente, después de haber seguido la serie de escaparates MVC, estoy usando repositorios, exponiendo IQueryable a una capa de servicio, que luego aplica filtros. Inicialmente he estado utilizando LINQtoSQL, p.¿Deberían los repositorios exponer IQueryable a la capa de servicio o realizar el filtrado en la implementación?

public interface IMyRepository 
{ 
    IQueryable<MyClass> GetAll(); 
} 

implementado en:

public class LINQtoSQLRepository : IMyRepository 
{ 
    public IQueryable<MyClass> GetAll() 
    { 
     return from table in dbContext.table 
      select new MyClass 
      { 
       Field1 = table.field1, 
       ... etc. 
      } 
    } 
} 

filtro para los ID:

public static class TableFilters 
{  
    public static MyClass WithID(this IQueryable<MyClass> qry, string id) 
    { 
     return (from t in qry 
       where t.ID == id 
       select t).SingleOrDefault(); 
    }  
} 

Llamado del servicio:

public class TableService 
{ 
    public MyClass RecordsByID(string id) 
    { 
     return _repository.GetAll() 
         .WithID(id); 
    } 
} 

me encontré con un problema cuando he experimentado con la aplicación de la repositorio usando Entity Framew ork con LINQ a Entidades. La clase de filtros en mi proyecto contiene algunas operaciones más complejas que el "DONDE ... == ..." en el ejemplo anterior, que creo que requieren diferentes implementaciones dependiendo del proveedor de LINQ. Específicamente, tengo el requisito de realizar una cláusula SQL "DONDE ... EN ...". Soy capaz de implementar esto en la clase de filtro usando:

string[] aParams = // array of IDs 
qry = qry.Where(t => aParams.Contains(t.ID)); 

Sin embargo, con el fin de llevar a cabo esto en contra de Entity Framework, necesito proporcionar una solución como la BuildContainsExpression que está vinculado a Entity Framework. Esto significa que tengo que tener 2 implementaciones diferentes de este filtro en particular, dependiendo del proveedor subyacente.

Agradecería cualquier consejo sobre cómo proceder desde aquí. Me pareció que exponer un IQueryable de mi repositorio me permitiría realizar filtros independientemente del proveedor subyacente, lo que me permite cambiar de proveedor si es necesario. Sin embargo, el problema que describo más arriba me hace pensar que debería realizar todos mis filtros dentro de los repositorios y devolver IEnumerable, IList o clases individuales.

Muchas gracias, Matt

+0

Falsas premisas. 'BuildContainsExpression' es * completamente * independiente del EF (depende solo de Linq y Expressions). También es completamente innecesario en EF 4, que admite 'IEnumerable.Contains' directamente. –

Respuesta

14

Esta es una pregunta muy popular. Uno que constantemente me pregunto a mí mismo. Siempre he considerado que es mejor devolver IEnumerable en lugar de IQueryable de un repositorio.

El propósito de un repositorio es encapsular la infraestructura de la base de datos para que el cliente no tenga que preocuparse por la fuente de datos. Sin embargo, si devuelve IQueryable, estará a merced del consumidor sobre qué tipo de consulta se ejecutará contra su base de datos, y si harán algo que el proveedor de LINQ no admita.

Tome la paginación, por ejemplo. Supongamos que tiene una entidad Cliente y su base de datos podría tener cientos de miles de clientes. ¿Qué código prefieres que escriba tu cliente?

var customers = repos.GetCustomers().Skip(skipCount).Take(pageSize).ToList(); 

O

var customers = repos.GetCustomers(pageIndex, pageSize); 

En la primera aproximación que hacen que sea imposible para el repositorio para restringir el número de registros recuperados del origen de datos. Además, su consumidor tiene que calcular el skipCount.

En el segundo enfoque, proporciona una interfaz más gruesa a su cliente.Ahora su repositorio puede aplicar algunas restricciones en el tamaño de página para optimizar la consulta. También encapsula el cálculo de skipCount.

Sin embargo, dicho esto, en su situación, su cliente es su servicio. Entonces, supongo que la pregunta realmente se reduce a una separación de preocupaciones. ¿Dónde es mejor realizar tal lógica de validación? Bueno, esa respuesta bien podría ser "en el servicio". Pero ¿qué pasa con la respuesta a "¿Dónde es mejor contener la lógica de consulta?". Para mí, la respuesta es claramente "The Repository". Esa es su área prevista de especialización.

Cuestiones relacionadas