Re[8]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 09:19
Оценка:
Z>Черта с два, ситуаций, когда мальчики должны быть в 100+ юзкейсах аналитики промаргивают значительно реже. Если это один юзкейс, то и запрос будет один.

у нас есть админка. в ней 10 страниц. на всех страницах должны отображаться только активные пользователи.
в случае прямых запросов, которые с таким подходом скорее всего будут писаться по месту, изменять придётся в этих 10 местах.
Re[10]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 09:43
Оценка:
Здравствуйте, Gengzu, Вы писали:

G>о каком бесполезном коде идёт речь? куда мы что засунем?

G>опять глупость. один GenericRepository обслуживает ВСЕ сущности в проекте. один класс с десятком методов по 1-3 строки каждый, пишется не так долго, правда?)
G>где проблемы? какие проблемы? репозиторий помогает унифицировать CRUD операции и абстрагироваться от хранилища данных. этого достаточно.

Так зачем абстрагировать CRUD, если для этого уже написан и отлажен ORM, он имеет отличный API для этого. Для чего абстрагировать CRUD через ORM?

Z>>Инкапсуляцию чего? Я привел кучу примеров, когда к неправильному коду приводит именно несвобода написать правильный затратив адекватные усилия. Жду примеров, когда писать неправильный код диктует свобода.


G>я не вижу ваших примеров. увы.

G>но в вашем случае, я уже писал. и чуть выше пример приводили. ну нет у вас метода на получение всех мальчиков старше 18 лет, ну и напишет паренёк Users.Where(u => u.Age > 18) по месту в методе, а метод этот вернёт этот же
результат в виде IQueriable дальше, где еще кто-то припишет запросик. какой-то.
G>получим и дублирование кода, и чёрти какие SQL запросы.

Я не вижу никакого дублирования. Нет никакой разницы в дублировании, напишет он вызов YongerThat или u.Age >. Если ограничение на 18 лет имеет свое бизнес название — нет никакой проблемы вынести его в именованый предикат. Репозитарий для этого совершенно не нужен. Черти какие запросы мы гарантированно получим максимально абстрагировав их от прикладного программиста. Он все равно выберет данные которые ему нужны, только ни о какой эффективности речи не будет, если у него в руках нет прямого способа сделать нужный запрос.

G>в случае использования спецификаций, достаточно будет изменить её одну, что бы получить необходимый результат во всех нужных местах.

G>плюс вся эта логика обычно обёрнута сервисным уровнем.
G>у нас есть админка. в ней 10 страниц. на всех страницах должны отображаться только активные пользователи.
G>в случае прямых запросов, которые с таким подходом скорее всего будут писаться по месту, изменять придётся в этих 10 местах.

Ты пойми, я тебя не уговариваю дублировать код. Я не понимаю, почему для избавления от дублирования нужны репозитарии. Не требуется никаких абстракций от CRUD, останется ровно тот же CRUD. Не требуется никаких абстракций, чтобы устранить дублирование кода запросов кроме экстеншенов к IQueryable, а абстрагироваться от него себе дороже. Что еще ты пытаешься абстрагировать за фасадом репозитария?
Re[9]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 09:45
Оценка:
Здравствуйте, Gengzu, Вы писали:

G>у нас есть админка. в ней 10 страниц. на всех страницах должны отображаться только активные пользователи.

G>в случае прямых запросов, которые с таким подходом скорее всего будут писаться по месту, изменять придётся в этих 10 местах.

У вас на каждой странице есть код который отображает этих пользователей? И кто тут говорил о вреде дублирования?
Re[10]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 09:47
Оценка:
Z>У вас на каждой странице есть код который отображает этих пользователей? И кто тут говорил о вреде дублирования?

по тем или иным критериям.

где-то мы просто список возвращаем, где-то отфильтрованных по каком-то критерию.
разные ситуации. но это вполне нормальный юзКейс.
Re[11]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 09:57
Оценка:
Здравствуйте, Gengzu, Вы писали:

Z>>У вас на каждой странице есть код который отображает этих пользователей? И кто тут говорил о вреде дублирования?


G>по тем или иным критериям.


G>где-то мы просто список возвращаем, где-то отфильтрованных по каком-то критерию.

G>разные ситуации. но это вполне нормальный юзКейс.

ок, поступило требование везде отображать активных пользователей, было 10 страниц с:
session.Users.Where(this page condition);

стало:
session.Users.Active().Where(this page condition);


Давай свою версию с репозитарием. Не забудь свое условие, что до этого у всех страниц были разные фильтры, либо они вообще отсутствовали.
Re[11]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 09:58
Оценка:
Z>Так зачем абстрагировать CRUD, если для этого уже написан и отлажен ORM, он имеет отличный API для этого. Для чего абстрагировать CRUD через ORM?

для абстрагирования от ORM.
другое дело, необходимо ли это.
зависит от задач.
для некоторых вполне позволительно прямое обращение к ORM, а где-то увы.
вариаций для чего, хватает.

Z>>>Инкапсуляцию чего? Я привел кучу примеров, когда к неправильному коду приводит именно несвобода написать правильный затратив адекватные усилия. Жду примеров, когда писать неправильный код диктует свобода.


Z>Я не вижу никакого дублирования. Нет никакой разницы в дублировании, напишет он вызов YongerThat или u.Age >. Если ограничение на 18 лет имеет свое бизнес название — нет никакой проблемы вынести его в именованый предикат.


собственно это и есть спецификации. другое дело, что предоставление IQueriable не накладывает ограничений на его применение, и пользователь волен поступать как ему заблагорассудится. в случае репозитария у него будет только один контролируемый выход.

Z>Репозитарий для этого совершенно не нужен. Черти какие запросы мы гарантированно получим максимально абстрагировав их от прикладного программиста. Он все равно выберет данные которые ему нужны, только ни о какой эффективности речи не будет, если у него в руках нет прямого способа сделать нужный запрос.


обычно ерунду люди творят тогда, когда могут. в моём случаи — они не могут.
конечно никто не защищён от того что он в любом месте заюзает чистый ADO.NET. но это крайности.
запросы с использованием репозитория вполне нормальные.
возможно у вас проблема в построении самой архитектуры, определении Aggregation Root'ов, либо непонимании предназначения паттерна Repository.
он, кстати, для простых веб-сайтов не очень подходит. да.

Z>Ты пойми, я тебя не уговариваю дублировать код. Я не понимаю, почему для избавления от дублирования нужны репозитарии. Не требуется никаких абстракций от CRUD, останется ровно тот же CRUD. Не требуется никаких абстракций, чтобы устранить дублирование кода запросов кроме экстеншенов к IQueryable, а абстрагироваться от него себе дороже. Что еще ты пытаешься абстрагировать за фасадом репозитария?


репозиторий абстрагирует нас от хранилища данных, и от ORM.
репозиторий не для избавления от дублирования, но в данном случае, и использованием IQueriable, он предохраняет от него. как минимум.
еще раз повторюсь, выставление и протаскивание IQueriable через все слои приводит к очень плохим sql запросам, плохо тестируемому коду, дублированию кода и размазаной логики. это же очевидно.
Re[12]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 10:07
Оценка:
Z>ок, поступило требование везде отображать активных пользователей, было 10 страниц с:
Z>
Z>session.Users.Where(this page condition);
Z>

Z>стало:
Z>
Z>session.Users.Active().Where(this page condition);
Z>


Z>Давай свою версию с репозитарием. Не забудь свое условие, что до этого у всех страниц были разные фильтры, либо они вообще отсутствовали.


repository.Find<User>(new ActiveUserSpecification())
было везде, как минимум. при необходимости мы меняем спецификацию, либо унаследованную от этой с добавлением условия, либо через new ActiveUserSpecification().And(new AgeMoreThen(18))

это даст гарантию того, что, когда поменяется условие отбора активных пользователей, например с простой проверки флага IsActive, на проверку еще по дате, мы везде получим ожидаемый результат.

и в вашем случае в общем-то тоже. но в вашем же случае, пользователь может очередной метод Active и не написать. зачем? ведь у него есть IQueriable, и можно сразу влепить запрос. в моём, увы, не сможет.

проблем тут заключается именно в возможности обхода.
Re[12]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 10:12
Оценка:
Здравствуйте, Gengzu, Вы писали:

G>репозиторий абстрагирует нас от хранилища данных, и от ORM.


Как тут хорошо процитировал Sinix:

They abstracted the Inversion of Control Container

(c)

вот еще полезная ссылка: http://www.joelonsoftware.com/articles/fog0000000018.html

G>репозиторий не для избавления от дублирования, но в данном случае, и использованием IQueriable, он предохраняет от него. как минимум.


Черт побери, IQueryable придумали, чтобы люди строили запросы с его помощью. А не для того, чтобы абстрагироваться от него за лесом тривиальных методов.

G>еще раз повторюсь, выставление и протаскивание IQueriable через все слои приводит к очень плохим sql запросам, плохо тестируемому коду, дублированию кода и размазаной логики. это же очевидно.


Совсем не очевидно. Настолько не очевидно, что я требую доказательств. Как и доказательство того, что запрос надо строить максимально далеко от слоя БД и протаскивать его через все слои попутно добавляя в них чего-то там. Я знаю, что так может получиться, если самостоятельно нарисовать кучу этих самых слоев, только вот зачем это делать?
Re[13]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 10:21
Оценка:
Z>Черт побери, IQueryable придумали, чтобы люди строили запросы с его помощью. А не для того, чтобы абстрагироваться от него за лесом тривиальных методов.

IQueriable очень хороший интерфейс, но увы, часто его использование приводит к непредсказуемым последствиям. и я не против его использования как такового, но я против протаскивания его через все слои приложения.

Z>Совсем не очевидно. Настолько не очевидно, что я требую доказательств. Как и доказательство того, что запрос надо строить максимально далеко от слоя БД и протаскивать его через все слои попутно добавляя в них чего-то там. Я знаю, что так может получиться, если самостоятельно нарисовать кучу этих самых слоев, только вот зачем это делать?


что именно не очевидно?

кто сказал что запрос нужно строить максимально далеко? наоборот. репозиторий лежит как можно ближе к нему. между ними только ORM.

имея IQueriable, человек начнёт писать запросы прямо в контроллере, например, при использовании ASP.NET MVC. и ведь начнёт. что приведёт опять же к размазанной дублируемой логики, плюс проблемы с тестированием.
Re[13]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 10:32
Оценка:
Здравствуйте, Gengzu, Вы писали:

Z>>Давай свою версию с репозитарием. Не забудь свое условие, что до этого у всех страниц были разные фильтры, либо они вообще отсутствовали.


G>repository.Find<User>(new ActiveUserSpecification())

G>было везде, как минимум. при необходимости мы меняем спецификацию, либо унаследованную от этой с добавлением условия, либо через new ActiveUserSpecification().And(new AgeMoreThen(18))

Вобщем для каждого поля сущности вы придумали написать руками тонну классов: AgeMoreThen, AgeLessThen, AgeEqualTo, AgeBetween, AgeIsNull какие еще? Только почему-то не учтен момент, что вместо ActiveUserSpecification кто-то напишет ActiveIsTrue.

G>и в вашем случае в общем-то тоже. но в вашем же случае, пользователь может очередной метод Active и не написать. зачем? ведь у него есть IQueriable, и можно сразу влепить запрос. в моём, увы, не сможет.


Ага, а результат тот же! Кроме того, что у меня программист может вместо введения/использования метода Active() написать Where(u => u.IsActive). Черт побери, вероятность этого, помноженная на вероятность того, что это создаст проблемы никак не сравнится с вредом проекту который утонул в множестве Specifications и разработке специального фреймворка для них. И уж совершенно точно комбинируя Specifications построить мало мальски сложный запрос нереально.

Если вдруг мне придется избавиться от флага IsActive, я совершенно точно буду вынужден просмотреть все использования этого флага. И это придется делать в любом случае. Особенно если они спрятаны во что-то типа (кстати, как вы различаете спецификации для разных сущностей?)
Re[14]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 10:43
Оценка:
Здравствуйте, Gengzu, Вы писали:

G>IQueriable очень хороший интерфейс, но увы, часто его использование приводит к непредсказуемым последствиям. и я не против его использования как такового, но я против протаскивания его через все слои приложения.


Еще раз. Зачем его протаскивать через все слои?

Z>>Совсем не очевидно. Настолько не очевидно, что я требую доказательств. Как и доказательство того, что запрос надо строить максимально далеко от слоя БД и протаскивать его через все слои попутно добавляя в них чего-то там. Я знаю, что так может получиться, если самостоятельно нарисовать кучу этих самых слоев, только вот зачем это делать?


G>что именно не очевидно?


Я русским языком написал что. Повторять не буду.

G>кто сказал что запрос нужно строить максимально далеко? наоборот. репозиторий лежит как можно ближе к нему. между ними только ORM.


Путаетесь в показаниях. Далеко говорилось не про репозитарий. Откуда появилось протаскивание через множество слоев в ваших рассуждениях?

G>имея IQueriable, человек начнёт писать запросы прямо в контроллере, например, при использовании ASP.NET MVC. и ведь начнёт. что приведёт опять же к размазанной дублируемой логики, плюс проблемы с тестированием.


А если и начнет? Где-то это вполне допустимо. На то они и контроллеры. Там где недопустимо — достаточно не инжектить в контроллер сессию, а инжектить сервисы, делающие нужные операции с БД. Это совсем не репозитарии, они абстрагируют бизнес логику, а не CRUD. Тестировать же операции с БД будет сложно в любом случае, в контроллере они или еще где.
Re[15]: Работа с ORM
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 26.07.11 11:10
Оценка: +1
Здравствуйте, Ziaw, Вы писали:

Z>Здравствуйте, Gengzu, Вы писали:


G>>IQueriable очень хороший интерфейс, но увы, часто его использование приводит к непредсказуемым последствиям. и я не против его использования как такового, но я против протаскивания его через все слои приложения.


Z>Еще раз. Зачем его протаскивать через все слои?


Затем что запрос эффективнее строить там где наиболее полно известна информация об этом запросе. А известна она на уровне UI.
Например:
1)Пользователь нажал на страницу 2 в списке каких-то объектов
2)В UI есть информация какой список объектов нужен и что вызвана страница 2
3)Код PL выполняет получение списка объектов из сервиса BL (IQueryable) и выполняет разбивку на страницы .Skip(...).Take(...), накладывает проекции, материализует для передачи UI.
3)Сервис BL получает от Repository IQueryable, фильтрует все Hidden объекты и накладывает предикаты видимости в зависимости от роли пользователя.
4)Repository является тонкой оберткой на ORM, чтобы в тестах его можно было заменить на что-то полегче.

Если не протаскивать IQueryable, то придется "протаскивать" передачу параметров из UI в нижние слои. Причем под каждый набор параметров понадобится свой метод, что приведет у увеличению количества методов, копипасте и другим плохим вещам.
Re[16]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 11:38
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Затем что запрос эффективнее строить там где наиболее полно известна информация об этом запросе. А известна она на уровне UI.

G>Например:
G>1)Пользователь нажал на страницу 2 в списке каких-то объектов
G>2)В UI есть информация какой список объектов нужен и что вызвана страница 2
G>3)Код PL выполняет получение списка объектов из сервиса BL (IQueryable) и выполняет разбивку на страницы .Skip(...).Take(...), накладывает проекции, материализует для передачи UI.
G>3)Сервис BL получает от Repository IQueryable, фильтрует все Hidden объекты и накладывает предикаты видимости в зависимости от роли пользователя.
G>4)Repository является тонкой оберткой на ORM, чтобы в тестах его можно было заменить на что-то полегче.

G>Если не протаскивать IQueryable, то придется "протаскивать" передачу параметров из UI в нижние слои. Причем под каждый набор параметров понадобится свой метод, что приведет у увеличению количества методов, копипасте и другим плохим вещам.


Протаскивается только предикат и протаскивается в одну сторону. Против этого я ничего не имею. Все протаскивание выглядит так PL->BL->DAL.

Обертку ORM для облегчения тестирования, конечно, сделать можно, только зачем называть ее Repository и делать для каждой сущности? Тем более мок фреймворки могут делать такие обертки на лету. Я же тестирую такие вещи прямо на базе с тестовыми данными, не идеально, но работает. Заодно позволяет заметить некоторые проблемы производительности.
Re[17]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 11:40
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Протаскивается только предикат и протаскивается в одну сторону. Против этого я ничего не имею. Все протаскивание выглядит так PL->BL->DAL.


Причем DAL это уже ORM, так что остается передача от одного слоя к другому. Правда с обработкой в слое безопасности.
Re[14]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 11:49
Оценка:
Z>Ага, а результат тот же!

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

а у тебя, скорее всего, каша.
Re[17]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 11:53
Оценка: :)
Z>зачем называть ее Repository и

если она реализует этот паттерн, то так её и следует называть. но никто не заставляет его юзать.

Z>делать для каждой сущности?


что за глупости?
Re[15]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 12:02
Оценка: +1 :)
Здравствуйте, Gengzu, Вы писали:

Z>>Ага, а результат тот же!


G>нет. результат не тот.

G>у меня все спецификации покрыты тестами. так же есть тесты на репозиторий. все сервисные методы так же покрыты тестами, так как известно какие типы и данные ходят, какие спецификации используются. контроллеры почти не содержат кода и так же легко поддаются тестированию.

Машушвать! Продемонстрируй ка тест на AgeLessThan и расскажи, что не покрыто тестами у меня? Ты сам то понял, что сейчас наконец расписался в том, что создал всю эту бодягу только для того, чтобы ее же радостно покрыть тестами? Готов спорить, что у тебя для тестов этой штуки придумана еще очень неслабая инфраструктура для тестирования.

У меня, между прочим, 100% покрытие всего этого и оно абсолютно не требует усилий, так как покрыть 0 строк кода стоит ровно ничего.

G>а у тебя, скорее всего, каша.


Тут спорить не буду. Тебе конечно, виднее.
Re[16]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 13:25
Оценка:
G>>нет. результат не тот.
G>>у меня все спецификации покрыты тестами. так же есть тесты на репозиторий. все сервисные методы так же покрыты тестами, так как известно какие типы и данные ходят, какие спецификации используются. контроллеры почти не содержат кода и так же легко поддаются тестированию.

Z>Машушвать! Продемонстрируй ка тест на AgeLessThan


а в чем проблема?

var spec = new ActiveUserSpecification();

var result = _users.Where(spec.IsSatisfiedBy());


Z> Ты сам то понял, что сейчас наконец расписался в том, что создал всю эту бодягу только для того, чтобы ее же радостно покрыть тестами?


я её могу покрыть тестами, что и делаю. это условие, часть бизнес логики. логично что на это должны быть тесты. и они есть.
твои методы так же можно покрыть.
но никто не заставляет твоих разработчиков юзать твои методы. они могут идти в обход, делая запросы к IQueriable. у меня не могут. вся разница.

Z> Готов спорить, что у тебя для тестов этой штуки придумана еще очень неслабая инфраструктура для тестирования.


поспорь.
Re[17]: Работа с ORM
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 26.07.11 15:04
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Здравствуйте, gandjustas, Вы писали:


G>>Затем что запрос эффективнее строить там где наиболее полно известна информация об этом запросе. А известна она на уровне UI.

G>>Например:
G>>1)Пользователь нажал на страницу 2 в списке каких-то объектов
G>>2)В UI есть информация какой список объектов нужен и что вызвана страница 2
G>>3)Код PL выполняет получение списка объектов из сервиса BL (IQueryable) и выполняет разбивку на страницы .Skip(...).Take(...), накладывает проекции, материализует для передачи UI.
G>>3)Сервис BL получает от Repository IQueryable, фильтрует все Hidden объекты и накладывает предикаты видимости в зависимости от роли пользователя.
G>>4)Repository является тонкой оберткой на ORM, чтобы в тестах его можно было заменить на что-то полегче.

G>>Если не протаскивать IQueryable, то придется "протаскивать" передачу параметров из UI в нижние слои. Причем под каждый набор параметров понадобится свой метод, что приведет у увеличению количества методов, копипасте и другим плохим вещам.


Z>Протаскивается только предикат и протаскивается в одну сторону. Против этого я ничего не имею. Все протаскивание выглядит так PL->BL->DAL.

Покажи как в коде будет выглядеть то что ты предлагаешь. Особенно интересуют проекции.

Z>Обертку ORM для облегчения тестирования, конечно, сделать можно, только зачем называть ее Repository и делать для каждой сущности?

А зачем для каждой сущности? Я примерно вот так делаю.

Z>Я же тестирую такие вещи прямо на базе с тестовыми данными, не идеально, но работает.

Тоже вариант, но писанины больше и работает медленнее.

Z>Заодно позволяет заметить некоторые проблемы производительности.

Тестировать производительность лучше всего на реальных данных, или приближенных к реальным, это уже будут не unit и даже не integration тесты.
Re[17]: Работа с ORM
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 26.07.11 15:49
Оценка:
Здравствуйте, Gengzu, Вы писали:

G>>>нет. результат не тот.

G>>>у меня все спецификации покрыты тестами. так же есть тесты на репозиторий. все сервисные методы так же покрыты тестами, так как известно какие типы и данные ходят, какие спецификации используются. контроллеры почти не содержат кода и так же легко поддаются тестированию.

Z>>Машушвать! Продемонстрируй ка тест на AgeLessThan


G>а в чем проблема?


G> var spec = new ActiveUserSpecification();


G> var result = _users.Where(spec.IsSatisfiedBy());


А почему бы не

var result = _users.Where(u => u.IsActive)

?

Или еще лучше
var result = _users.Active()

где
public static IQueryable<T> Active(this IQueryable<T> seq) where T:IActivateable, class
{
    return seq.Where(e => e.IsActive);
}

public interface IActivateable
{
    bool IsActive { get; set; }
}

class User: IActivateable //,..
{
    //...
}




Z>> Ты сам то понял, что сейчас наконец расписался в том, что создал всю эту бодягу только для того, чтобы ее же радостно покрыть тестами?


G>я её могу покрыть тестами, что и делаю. это условие, часть бизнес логики. логично что на это должны быть тесты. и они есть.

Не понятно? Ты сделал что-то, покрыл тестами, хотя без этого "что-то" то же самое проверяет компилятор.

G>твои методы так же можно покрыть.

G>но никто не заставляет твоих разработчиков юзать твои методы. они могут идти в обход, делая запросы к IQueriable. у меня не могут. вся разница.

var result = _users.Where(u => u.IsActive)

А result твой какого типа?
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.