Какие типы должны возвращать мой репозиторий Entity Framework и методы уровня обслуживания: List, IEnumerable, IQueryable? - PullRequest
9 голосов
/ 20 июня 2011

У меня есть конкретная реализация репозитория, которая возвращает IQueryable объекта:

public class Repository
{
    private AppDbContext context;

    public Repository()
    {
        context = new AppDbContext();
    }


    public IQueryable<Post> GetPosts()
    {
        return context.Posts;
    }
}

Мой уровень обслуживания может затем выполнять LINQ, как необходимо для других методов (где, пейджинг и т. Д.)

Прямо сейчас мой сервисный уровень настроен на возврат IEnumerable:

public IEnumerable<Post> GetPageOfPosts(int pageNumber, int pageSize)
{ 
    Repository postRepo = new Repository();

    var posts = (from p in postRepo.GetPosts()  //this is IQueryable
                orderby p.PostDate descending
                select p)
                .Skip((pageNumber - 1) * pageSize)
                .Take(pageSize);

    return posts;
}

Это означает, что в моем коде я должен сделать ToList (), если я хочу привязать к повторителю или другому элементу управления.

Это лучший способ обработки возвращаемых типов или мне нужно преобразовать в список, прежде чем я вернусь из методов уровня обслуживания?

Ответы [ 3 ]

19 голосов
/ 20 июня 2011

Оба подхода возможны, и это только вопрос выбора.

Как только вы используете IQueryable, у вас есть простой репозиторий, который будет работать в большинстве случаев, но он хуже тестируется, потому что запросы, определенные на IQueryable, являются linq-to-entity.Если вы имитируете репозиторий, они являются linq-to-objects в модульных тестах = вы не тестируете свою реальную реализацию.Вам нужны интеграционные тесты для проверки логики запросов.

Как только вы используете IEnumerable, у вас будут очень сложные общедоступные интерфейсы ваших репозиториев - вам потребуется специальный тип репозитория для каждой сущности, для которой требуется специальный запрос, представленный в репозитории.,Этот тип репозиториев более распространен в хранимых процедурах - каждый метод в репозитории был сопоставлен с одной хранимой процедурой.Этот тип репозитория обеспечивает лучшее разделение проблем и меньшую объемную абстракцию, но в то же время устраняет большую гибкость ORM и Linq.

В последнем случае вы можете использовать комбинированный подход, где у вас есть методы, возвращающие IEnumerable для наиболее распространенных сценариев (запросы используются чаще), и один метод, предоставляющий IQueryable для редких или сложных динамически создаваемых запросов.

Редактировать:

Как отмечено в комментариях, использование IQueryable имеет некоторые побочные эффекты.Когда вы выставляете IQueryable, вы должны поддерживать свой контекст в активном состоянии до тех пор, пока не выполните запрос - IQueryable использует отложенное выполнение таким же образом, как IEnumerable, поэтому, если вы не вызовете ToList, First или другие функции, выполняющие ваш запрос, вывсе еще нужен ваш контекст.

Самый простой способ добиться этого - использовать одноразовый шаблон в хранилище - создать контекст в его конструкторе и утилизировать его при удалении хранилища.Затем вы можете использовать using блоки и выполнять запросы внутри них.Этот подход предназначен для очень простых сценариев, когда вы довольны единым контекстом для каждого хранилища.Более сложные (и распространенные) сценарии требуют совместного использования контекста между несколькими репозиториями.В таком случае вы можете использовать что-то вроде провайдера контекста / фабрики (одноразового использования) и внедрить фабрику в конструктор репозитория (или разрешить провайдеру создавать репозитории).Это приводит к фабрике слоя DAL и пользовательской единице работы.

3 голосов
/ 21 июня 2011

Другое слово для вашего вопроса, кажется, должно определить, когда AppDbContext расположен или где он находится.

Если вы не утилизируете его, то есть он удаляется при выходе из приложения, нет проблем с возвратом IEnumerable / IQueryable, без фактических данных. Однако перед удалением AppDbContext вам нужно будет вернуть тип как IList с фактическими данными.

UPDATE: Я думаю, что вам нужно поймать следующий код, хотя вы уже знаете.

//outside of this code is refered to your code.

//Returning IEnumerable could be used outside this scope if AppDbContext is ensured no disposing
public IEnumerable<Post> GetIEnumerableWithoutActualData()
{
    return context.Posts;
}

//Even if AppDbContext is disposed, IEnumerable could be used.
public IEnumerable<Post> GetIEnumerableWithActualData()
{
    return context.Posts.ToList();
}
0 голосов
/ 20 июня 2011

Ваши типы возвращаемых данных всегда должны быть как можно выше в иерархии наследования ( или, может быть, я должен записать это как low , если основание направлено вниз ). Если все ваши методы требуют IQueryable<T>, то все возвращаемые значения должны сдавать этот тип.

Тем не менее, IEnumerable<T> имеет метод (AsQueryable()), который вы можете вызвать для достижения (как мне кажется,) желаемого результата.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...