.net - patrones - repositorios en c#
¿Deben los repositorios implementar IQueryable<T>? (3)
Estoy considerando una de las dos interfaces de IRepository, una que es descendiente de IQueryable y otra que contiene IQueryable.
Me gusta esto:
public interface IRepository<T> : IQueryable<T>
{
T Save(T entity);
void Delete(T entity);
}
O esto:
public interface IRepository<T>
{
T Save(T entity);
void Delete(T entity);
IQueryable<T> Query();
}
El uso de LINQ sería:
from dos
in ServiceLocator.Current.GetInstance<IRepository<DomainObject>>()
where dos.Id == id
select dos
O...
from dos
in ServiceLocator.Current.GetInstance<IRepository<DomainObject>>().Query
where dos.Id == id
select dos
Me gusta un poco el primero, pero es problemático burlarse. ¿Cómo han implementado otras personas repositorios de LINQable, mockable?
Depende si quieres una relación Has-A o Is-A.
El primero es una relación Is-A. La interfaz de IRepository es una interfaz IQueryable. El segundo es un has-a. El IRepository tiene una interfaz IQueryable. En el proceso de escribir esto, realmente me gusta el segundo mejor que el primero, simplemente porque cuando uso su segundo IRepository, puedo darle al método Query () CUALQUIER COSA que devuelva IQueryable. Para mí, eso es más flexible que la primera implementación.
Personalmente, utilizo el Repository Pattern
para devolver todos los elementos del Repositorio como IQueryable
. Al hacer esto, mi capa de repositorio ahora es muy, muy pequeña, pequeña ... con la capa de servicio (que consume la capa Repositorio) ahora puede abrirse a todos los tipos de manipulación de consultas.
Básicamente, toda mi lógica ahora se encuentra en la capa de servicio (que no tiene idea de qué tipo de repositorio usará ... y no quiere <<separación de preocupaciones) ... mientras que mi capa de repositorio solo trata con Obteniendo datos y Guardando datos en el repositorio (un servidor sql, un archivo, un satélite en el espacio, etc. <- más separación de preocupaciones).
p.ej. Más o menos pseduo código ya que estoy recordando lo que hemos hecho en nuestro código y simplificándolo para esta respuesta ...
public interface IRepository<T>
{
IQueryable<T> Find();
void Save(T entity);
void Delete(T entity);
}
y tener un repositorio de usuario ...
public class UserRepository : IRepository<User>
{
public IQueryable<User> Find()
{
// Context is some Entity Framework context or
// Linq-to-Sql or NHib or an Xml file, etc...
// I didn''t bother adding this, to this example code.
return context.Users().AsQueryable();
}
// ... etc
}
y ahora para el mejor bit :)
public void UserServices : IUserServices
{
private readonly IRepository<User> _userRepository;
public UserServices(IRepository<User> userRepository)
{
_userRepository = userRepository;
}
public User FindById(int userId)
{
return _userRepository.Find()
.WithUserId(userId)
.SingleOrDefault(); // <-- This will be null, if the
// user doesn''t exist
// in the repository.
}
// Note: some people might not want the FindBySingle method because this
// uber method can do that, also. But i wanted to show u the power
// of having the Repository return an IQuerable.
public User FindSingle(Expression<Func<User, bool>> predicate)
{
return _userRepository
.Find()
.SingleOrDefault(predicate);
}
}
Puntos de bonificación: ¿WTF es WithUserId(userId)
en el método FindById
? Eso es un tubo y filtro . Úsalos :) les encanta :) abrázalos :) hacen que tu código sea MUY legible :) Ahora, si quieres saber qué es lo que hace ... este es el método de extensión.
public static User WithId(this IQueryable<User> source, int userId)
{
return source.Where(u => u.UserId == userId).SingleOrDefault();
}
HTH a pesar de que esta pregunta es ... bueno ... casi dos años de edad :)
Siempre puedes escribir rápidamente cosas en contra de List, no es burlarse usando un framework simulado, pero seguro que funciona genial.