Чтобы вернуть IQueryable<T> или нет, вернуть IQueryable<T>

У меня есть класс репозитория, который оборачивает мой LINQ to SQL Data Context. Класс репозитория является классом бизнес-линии, который содержит всю логику уровня данных (и кеширование и тому подобное).

Вот мой v1 моего интерфейса репо.

public interface ILocationRepository
{
    IList<Location> FindAll();
    IList<Location> FindForState(State state);
    IList<Location> FindForPostCode(string postCode);
}

Но чтобы справиться с поиском по страницам для FindAll, я спорю о том, использовать ли IQueryable вместо IList для упрощения интерфейса при таких обстоятельствах, как подкачка страниц.

Каковы преимущества и недостатки предоставления IQueryable из хранилища данных?

Любая помощь очень ценится.

3 ответа

Решение

Плюсы; компонуемости:

  • абоненты могут добавлять фильтры
  • абоненты могут добавить пейджинг
  • абоненты могут добавить сортировку
  • так далее

Минусы; без проверяемость:

  • Ваш репозиторий больше не является надлежащим образом тестируемым модулем; Вы не можете положиться на: это работает, b: что он делает;
    • вызывающая сторона может добавить непереводимую функцию (т.е. нет отображения TSQL; разрывы во время выполнения)
    • вызывающая сторона может добавить фильтр / сортировку, которая заставляет его работать как собака
  • Так как абоненты ожидают IQueryable<T> чтобы быть компонуемым, он исключает нескомпозиционные реализации или заставляет вас написать свой собственный поставщик запросов для них
  • это означает, что вы не можете оптимизировать / профилировать DAL

Для стабильности я взял на себя не выставлять IQueryable<T> или же Expression<...> в моих репозиториях. Это означает, что я знаю, как ведет себя репозиторий, и мои верхние уровни могут использовать макеты, не беспокоясь "поддерживает ли реальный репозиторий это?" (форсирование интеграционных тестов).

Я все еще использую IQueryable<T> и т.д. внутри хранилища - но не за границей. Я разместил еще несколько мыслей на эту тему здесь. Настроить параметры разбиения на страницы в интерфейсе хранилища также просто. Вы можете даже использовать методы расширения (в интерфейсе), чтобы добавить необязательные параметры подкачки, чтобы у конкретных классов был только один реализуемый метод, но для вызывающей стороны может быть 2 или 3 перегрузки.

Как упоминалось в предыдущем ответе, разоблачение 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);
}

если размер == -1, тогда вернуть все...

Альтернативный способ...

Если вы все еще хотите вернуть IQueryable, то вы можете вернуть IQueryable из List внутри своих функций... например...

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
    }
}

Первый метод имеет преимущество перед памятью, потому что вы будете возвращать меньше данных вместо всех.

Я рекомендую использовать IEnumerable вместо IListС этим у вас будет больше гибкости.

Таким образом, вы сможете получить от Db только ту часть данных, которую вы действительно собираетесь использовать, без дополнительной работы, выполненной в вашем хранилище.

Образец:

// Repository
public interface IRepository
{
    IEnumerable<Location> GetLocations();
}

// Controller
public ActionResult Locations(int? page)
{
    return View(repository.GetLocations().AsPagination(page ?? 1, 10);
}

Который очень чистый и простой.

Другие вопросы по тегам