26 / 26 / 5
Регистрация: 29.05.2013
Сообщений: 151
1

Рассуждения о Generic Repository паттерне

02.10.2014, 21:07. Показов 4234. Ответов 19
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Добрый день.
Хотелось бы прояснить для себя следующий момент. Вот есть у нас обобщенный репозиторий
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class
    {
        private MyDbContext context;
        private DbSet<TEntity> dbSet;
 
        public GenericRepository(MyDbContext dbcontext)
        {
            this.context = dbcontext;
            this.dbSet = context.Set<TEntity>();
        }
 
        public IQueryable<TEntity> FindAll()
        {
              return dbSet;
         }
с методом, например:

C#
1
2
3
4
public IQueryable<TEntity> FindAll()
{
       return dbSet;
}
Этот метод возвращает, скажем, все объекты DbSet<Products> нашего DbContext. Далее, допустим создаю я в коде объект репозитория:

C#
1
2
MyDbContext context = new MyDbContext();
GenericRepository<Product> productRepository = new GenericRepository<Product> (context);
и делаю следующий запрос, например:

C#
1
var query = productRepository.FindAll().Where(p=>p.ID==23).ToList()
Правильно ли я понимаю выполнение этого запроса: в память загружаются все сущности Продукт (не взирая на то, что FindAll возвращает IQueryable ), а уже в памяти отбираются сущности с Айди равным 23?

Или, все-таки, поскольку FindAll возвращает IQueryable, и т.к. Where мы вызываем на объекте IQueryable - то отбор по Where происходит в БД?

Оченно интересный вопрос.... Спасибо.
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
02.10.2014, 21:07
Ответы с готовыми решениями:

Generic Repository без Entity
Не могу понять как реализовать Generic Repository без Entity Framework

Спецификация generic upcast/downcast в generic типах
Почему только интерфейсы и делегаты в C# поддерживают технику ковариантности и контрвариантности в ...

Вопрос о паттерне MVP
Здравствуйте, уважаемые программисты. Порекомендуйте, пожалуйста, ссылки о MVP паттерне для Windows...

Отрицание конкретного слова в паттерне
никак не могу найти возможность отрицания конкретного слова в паттерне Пример: s=&quot;hellow world&quot;...

19
325 / 136 / 28
Регистрация: 18.09.2014
Сообщений: 167
02.10.2014, 21:16 2
Where на IQueryable приведет к фильтрации множества на стороне источника данных (базы данных)
Where на IEnumerable приведет к запросу полного множества и фильтрации его на клиенте.

В Вашем случае Where вызывается на IQueryable, значит, отбор произойдет в БД.

Есть видео по этой теме: https://www.youtube.com/watch?v=hIjtj3b8XPg
Примерно с 18 минуты как раз на примере показывается разница.
0
26 / 26 / 5
Регистрация: 29.05.2013
Сообщений: 151
02.10.2014, 21:34  [ТС] 3
Defazze, Спасибо за отклик! Разницу между Нумерабле и Кверибл я понимаю вроде... Собственно так, как вы написали - я и думал. Но тут вот в чем вопрос:

нафига каждый освещающий паттерн Дженерик Репозитори в сети непременно снабжает его дополнительными методами типа:

C#
1
2
3
4
5
public IQueryable<TEntity> GetManyByFilter(Expression<Func<TEntity, bool>> filter)
        {
            IQueryable<TEntity> query = dbSet.Where(filter);
            return query;
        }
и прочими? Или я таки не прав?
0
325 / 136 / 28
Регистрация: 18.09.2014
Сообщений: 167
02.10.2014, 21:38 4
А как иначе выполнять фильтрацию по условию в рамках этого паттерна?

Не по теме:

не говоря уже о том, что лично у меня есть большие сомнения в полезности данного паттерна )

0
26 / 26 / 5
Регистрация: 29.05.2013
Сообщений: 151
02.10.2014, 21:49  [ТС] 5
Цитата Сообщение от Defazze Посмотреть сообщение
А как иначе выполнять фильтрацию по условию в рамках этого паттерна?
Да вот так и выполнять:
C#
1
2
productRepository.FindAll().Where(p=>p......)
productRepository.FindAll().GroupBy(p=>p......)
На FindAll вызываем любой метод ЛИНК и зачем эти GetManyByFilter если отбор происходит все-равно на стороне БД. Или я чего то недопонял?

Добавлено через 4 минуты
Вот кстати запрос для var query через productRepository.FindAll().Where(p=>p.ID==3)

C#
1
2
3
4
5
6
query   {SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[Adress] AS [Adress]
    FROM [dbo].[Products] AS [Extent1]
    WHERE 3 = [Extent1].[ID]}
и через productRepository.GetManyByFilter(3)
C#
1
2
3
4
5
6
query   {SELECT 
    [Extent1].[ID] AS [ID], 
    [Extent1].[Name] AS [Name], 
    [Extent1].[Adress] AS [Adress]
    FROM [dbo].[Products] AS [Extent1]
    WHERE 3 = [Extent1].[ID]}
Они совершенно одинаковы...
0
325 / 136 / 28
Регистрация: 18.09.2014
Сообщений: 167
02.10.2014, 21:52 6
Совершенно верно. И тут возникает вопрос: а зачем тогда репозиторий? Какая разница между вызовами этих методов и
C#
1
dbContext.Products.Where(...)
0
26 / 26 / 5
Регистрация: 29.05.2013
Сообщений: 151
02.10.2014, 21:56  [ТС] 7
Цитата Сообщение от Defazze Посмотреть сообщение
И тут возникает вопрос: а зачем тогда репозиторий?
Для юнит-тестирования полезен, его легко можно подменить на имитацию.
0
325 / 136 / 28
Регистрация: 18.09.2014
Сообщений: 167
02.10.2014, 21:59 8
С моей точки зрения, репозиторий должен возвращать конечный набор сущностей, т.е. IEnumerable. Инкапсулируя в себе полностью всю работу с источником данных. Тогда и юнит-тесты будет писать легко и приятно, и не возникнет никаких скрытых запросов к БД через торчащий наружу IQueryable.

Но это сугубо моё имхо.
0
26 / 26 / 5
Регистрация: 29.05.2013
Сообщений: 151
02.10.2014, 23:25  [ТС] 9
Благодарю за беседу! Нужно вашу последнюю мысль обмозговать... Похоже, вы правы...

Добавлено через 1 час 19 минут
Defazze,
Я мало-мало подумал и вот что пришло в голову... как сразу не сообразил, перегрев, видать... Преимущество Репозитория то основное не в юнит-тестинге а в слабой связанности компонентов. Репозиторий то инкапсулирует источник данных, как вы справедливо заметили. Так вот если в Контроллерах вы будете использовать DbContext, то при смене источника данных вам прийдется долго править стопитсот Контроллеров. В сслучае же передачи интерфейса Репозитория в Контроллер вам нужно будет лишь написать новую "прокладку", реализующую этот интерфейс для нового источника данных.

И вот тут таки вы правы - IQueryable торчать не должен. Хотя... почему не должен? Вполне может себе и торчать, это ни на что не повлияет, и на тестирование в том числе. Мы при тестировании что проверяем? Правильность возвращенного методом Контроллера набора данных. Что нам тот торчащий IQueryable? Он может помешать только при изменении предметной области... Но при таком изменении и IEnumerable с GetByFilter не спасет - все одно прийдется править.

Т.о. GetByFilter таки нафик не нужны, а Репозиторий пользителен (как часто меняется источник данных - не обсуждаем). Ну и пейсатели из блогов про Дженерик Репозитори пишут, получается, сами не зная что.

Или я таки не прав?
0
325 / 136 / 28
Регистрация: 18.09.2014
Сообщений: 167
02.10.2014, 23:35 10
Я ж не против репозиториев. Они очень даже полезны - как еще один слой абстракции (если этот слой необходим). Насчет IQueryable - с моей точки зрения, задача репозитория - возвращать конечное множество. Слой бизнес-логики не должен формировать запросы к БД. Он отдал репозиторию параметры, получил данные, всё. А торчащий IQueryable приводит к тому, что в бизнес-логике возникают монстры типа
C#
1
2
3
4
var result = from p in productRepository.FindAll()
join c in customerRepository.FindAll() on p.CustomerId = c.CustomerId
where c.CustomerId=8
select p;
ну и чем это лучше чистой работы с моделью?

А паттерн, ну да, есть такой паттерн. Можно применять. Особой пользы это не приносит.
0
26 / 26 / 5
Регистрация: 29.05.2013
Сообщений: 151
03.10.2014, 00:14  [ТС] 11
Цитата Сообщение от Defazze Посмотреть сообщение
var result = from p in productRepository.FindAll()
join c in customerRepository.FindAll() on p.CustomerId = c.CustomerId
where c.CustomerId=8
select p;
Пардон, есть же Навигэйшн Пропертиc:

C#
1
var result=productRepository.FindAll().Where(p=>p.Customer.CustomerID==8)
Добавлено через 26 минут
Defazze,
а как вы объект dbContext получаете в Контроллере?
0
325 / 136 / 28
Регистрация: 18.09.2014
Сообщений: 167
03.10.2014, 08:05 12
да, есть навигационные свойства. Но они ситуацию не улучшают. Сравните со следующим кодом:
C#
1
List<Product> products = customerRepository.GetProducts(8);
Всё просто и понятно. Никаких IQueryable и where. Надо написать юнит-тест - делаем один мок на GetProducts, сразу возвращая нужный нам для тестов список. В вашем же случае надо создать список, превратить его в IQueryable, а потом еще и проверить, что он корректно обрабатывается инструкцией .Where(p=>p.Customer.CustomerID==8) - только тогда мы получим корректный мок.

Я с dbContext работаю только на уровне репозиториев. Ни бизнес-логика, ни тем более контроллеры о нем понятия не имеют.
1
26 / 26 / 5
Регистрация: 29.05.2013
Сообщений: 151
03.10.2014, 14:37  [ТС] 13
Defazze, огромное вам спасибо! Я че то заблукал малость. Беседа с вами помогла все разложить по полочкам и сформировать четкую картину.

Цитата Сообщение от Defazze Посмотреть сообщение
Я с dbContext работаю только на уровне репозиториев. Ни бизнес-логика, ни тем более контроллеры о нем понятия не имеют.
Да, да, я просто не правильно понял ваше высказывание
Цитата Сообщение от Defazze Посмотреть сообщение
Какая разница между вызовами этих методов и
Код C#
1
dbContext.Products.Where(...)
я от недосыпа подкмал, что вы предлагаете использовать непосредственно контекст в контроллере.

Вы безусловно правы, методы репозитория должны возвращать конечный набор. И по торчащему IQueryable тоже правы. Ну а Дженерик - действительно ненужная штука. А при большом кол-ве сущностей предметной области еще и вредная: его реализация в таком случае в силу отличий между сущностями выльется в черт те что и черт те как.

Еще раз спасибо!

Добавлено через 1 час 3 минуты
Не, ну а пейсатели все равно такие пейсатели. У них же у 90 процентов IQueryable возвращается из Дженерик Репозитори... А у многих все одно потом еще ЛИНК на этот Кверибл навинчивается, прошивая все насквозь...
0
Заблокирован
06.10.2014, 20:38 14
Defazze, немного не понял, где нужно возвращать IEnumerable из репозитория? в каких случаях?
если я везде так буду делать у меня кончится память. я понял, что IQueryable для уменьшения накладных расходов на память, а IEnumerable для увеличения скорости запроса.

Добавлено через 2 минуты
или я немного о другом?
0
325 / 136 / 28
Регистрация: 18.09.2014
Сообщений: 167
06.10.2014, 20:59 15
я понял, что IQueryable для уменьшения накладных расходов на память, а IEnumerable для увеличения скорости запроса.
Это совершенно не так. Более того, открою страшную тайну: IQueryable одновременно является и IEnumerable, т. к. "наследуется" от него.
0
Заблокирован
06.10.2014, 21:43 16
если в базе данных мало данных, а сами сущности занимают много места в памяти?
C#
1
var someData = context.Where(sd => sd.Prop == num); //IEnumerable
SQL
1
SELECT * FROM
C#
1
var someData = context.Where(sd => sd.Prop == num); //Queryable
SQL
1
2
SELECT * FROM 
WHERE PROP
я понял что нужно тестировать короче, все равно все на свете не может быть IEnumerable

Добавлено через 1 минуту
Defazze, или я что-то непонимаю
0
325 / 136 / 28
Регистрация: 18.09.2014
Сообщений: 167
06.10.2014, 22:11 17
Вот здесь:
C#
1
var someData = context.Where(sd => sd.Prop == num);
Вы же всё равно придете к IEnumerable, когда будете перебирать полученный результат в цикле или превращать его в список.

Т.е. конечным итогом работы IQueryable всё равно является IEnumerable.
0
Заблокирован
06.10.2014, 22:19 18
Defazze, т.е если элемент один, то один и превратится в IEnumerable?
0
325 / 136 / 28
Регистрация: 18.09.2014
Сообщений: 167
06.10.2014, 22:48 19
ну это смотря как преобразовывать. Если ToList, то будет List. Если First, Single и пр., то будет один элемент.
0
197 / 197 / 5
Регистрация: 24.07.2010
Сообщений: 1,311
06.11.2014, 22:57 20
У вас недоделанный репозиторий, лучше делать не класс генерик, а методы
0
06.11.2014, 22:57
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
06.11.2014, 22:57
Помогаю со студенческими работами здесь

Панель управления в MVC-паттерне
Есть панель MVC администрирования, необходимо прикрутить её к сайту. Куда поместить эту самую...

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

Использование диалогов в MVVM паттерне
Собственно к какой части модели относится диалог? View или ViewModel? И как лучше реализовать? Что...

Путь к файлам в MVC паттерне
Извините, что тема не в &quot;Фреймоврках&quot; - там все умерли :sleep: Написал движек используя zf1.*....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru