Должны ли хранилища реализовать IQueryable <T>?
Я рассматриваю один из двух интерфейсов IRepository, один из которых является потомком IQueryable, и тот, который содержит IQueryable.
Вот так:
public interface IRepository<T> : IQueryable<T>
{
T Save(T entity);
void Delete(T entity);
}
Или это:
public interface IRepository<T>
{
T Save(T entity);
void Delete(T entity);
IQueryable<T> Query();
}
Использование LINQ будет:
from dos
in ServiceLocator.Current.GetInstance<IRepository<DomainObject>>()
where dos.Id == id
select dos
Или...
from dos
in ServiceLocator.Current.GetInstance<IRepository<DomainObject>>().Query
where dos.Id == id
select dos
Мне нравится первый, но это проблематично издеваться. Как другие люди реализовали LINQable, mockingable repositories?
Ответы
Ответ 1
Зависит от того, хотите ли вы отношения Has-A или Is-A.
Первый - это отношение Is-A. Интерфейс IRepository - это интерфейс IQueryable. Второй - has-a. IRepository имеет интерфейс IQueryable. В процессе написания этого, мне на самом деле нравится второй лучше, чем первый, просто потому, что, когда вы используете свой второй IRepository, я могу передать метод Query() AnyYING, который возвращает IQueryable. Для меня это более гибкая, чем первая реализация.
Ответ 2
Лично я использую Repository Pattern
для возврата всех элементов из репозитория в качестве IQueryable
. Таким образом, мой слой репозитория теперь очень легкий, маленький.. со слоем обслуживания (который поглощает уровень репозитория) теперь можно открыть для всех типов манипуляций с запросами.
В принципе, вся моя логика теперь находится на уровне сервиса (который понятия не имеет, какой тип репозитория он будет использовать.. и не хочет знать < - разделение проблем), тогда как мой уровень репозитория просто занимается получением данных и сохранением данных в репо (сервер sql, файл, спутник в космосе.. и т.д. - большее разделение проблем).
например. Более или менее код pseduo, поскольку я помню, что мы сделали в нашем коде и упростили его для этого ответа...
public interface IRepository<T>
{
IQueryable<T> Find();
void Save(T entity);
void Delete(T entity);
}
и иметь пользовательский репозиторий...
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
}
и теперь для лучшего бита:)
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);
}
}
Бонусные баллы: WTF WithUserId(userId)
в методе FindById
? Это Труба и фильтр. Используйте их:) любите их:) обнимайте их:) Они делают ваш код SOOO очень читаемым:) Теперь, если вы хотите узнать, что это делает... это метод расширения.
public static User WithId(this IQueryable<User> source, int userId)
{
return source.Where(u => u.UserId == userId).SingleOrDefault();
}
HTH, хотя этот вопрос... ну... почти два года:)
Ответ 3
Вы всегда можете быстро писать материал против List, но не издеваться над фреймворком, но он отлично работает.