Чтобы вернуть IQueryable <T> или не вернуть IQueryable <T>
У меня есть класс репозитория, который переносит мой LINQ в SQL Data Context. Класс репозитория - это класс бизнес-линии, который содержит всю логику уровня данных (и кэширование и т.д.).
Здесь мой v1 моего интерфейса репо.
public interface ILocationRepository
{
IList<Location> FindAll();
IList<Location> FindForState(State state);
IList<Location> FindForPostCode(string postCode);
}
Но для обработки пейджинга для FindAll я обсуждаю, следует ли выставлять IQueryable <ILocation> вместо IList, чтобы упростить интерфейс для таких ситуаций, как пейджинг.
Каковы плюсы и минусы для раскрытия IQueryable из репо данных?
Любая помощь очень ценится.
Ответы
Ответ 1
Профи; компонуемости:
- абоненты могут добавлять фильтры
- вызывающие могут добавить пейджинг
- вызывающие могут добавлять сортировку
- и т.д.
Против; без проверяемость:
- Ваш репозиторий более не поддается тестированию; вы не можете полагаться на: он работает, b: что он делает;
- вызывающий может добавить непереводимую функцию (т.е. не отображать TSQL, перерывы во время выполнения)
- вызывающий может добавить фильтр/сортировку, которая заставляет его выполнять как собаку
- Поскольку вызывающие абоненты ожидают, что
IQueryable<T>
будет составным, он исключит неконсолидируемые реализации - или он заставит вас написать свой собственный поставщик запросов для них.
- это означает, что вы не можете оптимизировать/профиль DAL
Для стабильности я перешел на не отображение IQueryable<T>
или Expression<...>
в моих репозиториях. Это означает, что я знаю, как работает репозиторий, а мои верхние уровни могут использовать mocks, не беспокоясь о том, поддерживает ли реальный репозиторий это? (принудительные интеграционные тесты).
Я все еще использую IQueryable<T>
и т.д. внутри репозитория - но не над границей. Я добавил несколько больше мыслей по этой теме здесь. Также легко задать параметры поискового вызова в интерфейсе репозитория. Вы можете даже использовать методы расширения (на интерфейсе), чтобы добавить необязательные параметры поискового вызова, чтобы конкретные классы имели только один способ реализации, но может быть 2 или 3 перегрузки, доступные для вызывающего.
Ответ 2
Как уже упоминалось в предыдущем ответе, просмотр IQueryable дает доступ к вызывающим абонентам для игры с самим IQueryable, что является или может стать опасным.
Инкапсуляция бизнес-логики в первую очередь заключается в поддержании целостности вашей базы данных.
Вы можете продолжить публикацию IList и изменить свои параметры следующим образом: вот как мы это делаем...
public interface ILocationRepository
{
IList<Location> FindAll(int start, int size);
IList<Location> FindForState(State state, int start, int size);
IList<Location> FindForPostCode(string postCode, int start, int size);
}
if size == -1, а затем вернуть все...
Альтернативный способ...
Если вы все еще хотите вернуть IQueryable, вы можете вернуть IQueryable из списка внутри своих функций.. например...
public class MyRepository
{
IQueryable<Location> FindAll()
{
List<Location> myLocations = ....;
return myLocations.AsQueryable<Location>;
// here Query can only be applied on this
// subset, not directly to the database
}
}
Первый метод имеет преимущество перед памятью, потому что вы будете возвращать меньше данных вместо всех.
Ответ 3
Я рекомендую использовать IEnumerable
вместо IList
, при этом у вас будет больше гибкости.
Таким образом, вы сможете получить от Db только ту часть данных, которую вы действительно собираетесь использовать без дополнительной работы в вашем репозитории.
Пример:
// Repository
public interface IRepository
{
IEnumerable<Location> GetLocations();
}
// Controller
public ActionResult Locations(int? page)
{
return View(repository.GetLocations().AsPagination(page ?? 1, 10);
}
Это супер чистое и простое.