Работа с ORM
От: Аноним  
Дата: 26.07.11 04:31
Оценка:
Решили в проекте использовать ORM (NHibernate) и возник вопрос как реализовать доступ к объектам.
Подскажите какое из решений лучше:

1) Реализовать все как св-ва IUnitOfWork

public interface IUnitOfWork
{
   ....
   IQueryable<User> Users{get;}
}


использование:

public UserRepository:IUserRepository
{
......
    public IQueryable<User> Items
    {
       return _iUnitOfWork.Users;
    }
}

И здесь возникает вопрос насколько правильно использовать в методе сервиса свойства UOW в обход репозитариев:


class UserService
{
somemethod()
{
   var t=from u in  IUnitOfWork.Users
         ...join .. IUnitOfWork.Pages
        where...
        .... select u
}
}



2)Реализовать непосредственно в репозитории:
public UserRepository:IUserRepository
{
......
    public IQueryable<User> Items
    {
       return _session.Query<User>();
    }
}


3) Подвариант 2-го использовать дженериковский репозиторий
Re: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 05:47
Оценка: 1 (1)
Здравствуйте, Аноним, Вы писали:

А>Решили в проекте использовать ORM (NHibernate) и возник вопрос как реализовать доступ к объектам.

А>Подскажите какое из решений лучше:

Сессия и есть UnitOfWork, ничего не надо больше ничего абстрагировать. Максимум можно свой потомок сделать, с со свойствами IQueryable<Entity> и то, лучше обойтись extension methods.

Репозитарии только увеличивают размер кода, не решая никакой архитектурной проблемы, являясь рудиментом рукописных ORM, призваных, в свое время избавить код бизнес логики от конструирования SQL. Вдобавок ко всему, репозитарии подталкивают к использованию навигационного доступа к БД, что резко увеличивает нагрузку на базу и превращает приложение в тормозное УГ.

Берите сессию и используйте ее в хвост и в гриву прямо в логике. Можно абстрагировать только время ее жизни в какой-либо легковесный IoC контейнер.
Re: Работа с ORM
От: Aviator  
Дата: 26.07.11 06:44
Оценка:
Здравствуйте, Аноним, Вы писали:

Разработчики NHibernate уже реализовали unit of work и назвали его ISession.
Re[2]: Работа с ORM
От: Аноним  
Дата: 26.07.11 07:05
Оценка:
Здравствуйте, Aviator, Вы писали:

A>Здравствуйте, Аноним, Вы писали:


A>Разработчики NHibernate уже реализовали unit of work и назвали его ISession.


Да, я это понимаю, но хочется иметь некую абстракцию в коде:


using (IUnitOfWork uow = UnitOfWork.CreateUow())
{
   uow.BeginTransaction();
   .......
   uow.CommitTransaction();
}
Re: Работа с ORM
От: Gengzu  
Дата: 26.07.11 07:10
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Решили в проекте использовать ORM (NHibernate) и возник вопрос как реализовать доступ к объектам.

А>Подскажите какое из решений лучше:

А>3) Подвариант 2-го использовать дженериковский репозиторий


3й вариант предпочтительней, ибо вносит большую прозрачность в код. к тому же при необходимости может будет заменить ORM на другой, переписав лишь репозиторий. либо вообще переход на NoSQL. Другое дело если ничего такого не предвидится никогда.

выносить наружу IQueriable плохо тем, что любой разработчик сможет дописать что угодно в запрос, а это приведёт в свою очередь как к дублированию кода, так и к кривым SQL запросам.
Re[3]: Работа с ORM
От: Aviator  
Дата: 26.07.11 07:17
Оценка:
Здравствуйте, Аноним, Вы писали:

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


A>>Здравствуйте, Аноним, Вы писали:


A>>Разработчики NHibernate уже реализовали unit of work и назвали его ISession.


А>Да, я это понимаю, но хочется иметь некую абстракцию в коде:



А>
А>using (IUnitOfWork uow = UnitOfWork.CreateUow())
А>{
А>   uow.BeginTransaction();
А>   .......
А>   uow.CommitTransaction();
А>}
А>


И чем это противоречит использованию сессии в репозитариях? Имейте хоть десять абстракций, главное что бы они шарили одну сессию.
Re[2]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 07:20
Оценка:
Здравствуйте, Gengzu, Вы писали:

G>3й вариант предпочтительней, ибо вносит большую прозрачность в код. к тому же при необходимости может будет заменить ORM на другой, переписав лишь репозиторий. либо вообще переход на NoSQL. Другое дело если ничего такого не предвидится никогда.


Это фантастика. Просто так взять и поменять ORM может получиться только в очень простых приложениях. А поддерживать абстракцию и бороться с ней придется весь цикл жизни приложения.

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


А не выносить — приведет к навигационному доступу и куче однострочников которые используются в одном месте.
Re[2]: Работа с ORM
От: Aviator  
Дата: 26.07.11 07:22
Оценка:
Здравствуйте, Gengzu, Вы писали:

G>Здравствуйте, Аноним, Вы писали:


А>>Решили в проекте использовать ORM (NHibernate) и возник вопрос как реализовать доступ к объектам.

А>>Подскажите какое из решений лучше:

G>3й вариант предпочтительней, ибо вносит большую прозрачность в код. к тому же при необходимости может будет заменить ORM на другой, переписав лишь репозиторий. либо вообще переход на NoSQL. Другое дело если ничего такого не предвидится никогда.

Это из области фантастики, прозрачно сменить ОРМ получится только в случае очень простых запросов.

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

Да, это почти равноценно использованию sql запросов в коде.
Re[3]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 07:24
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>Да, я это понимаю, но хочется иметь некую абстракцию в коде:


А>
А>using (IUnitOfWork uow = UnitOfWork.CreateUow())
А>{
А>   uow.BeginTransaction();
А>   .......
А>   uow.CommitTransaction();
А>}
А>


Что вам даст эта абстракция? На какой ORM вы сможете поменять NHibernate? Какие детали NHibernate она скроет?
Re[3]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 07:25
Оценка:
Z>Это фантастика. Просто так взять и поменять ORM может получиться только в очень простых приложениях. А поддерживать абстракцию и бороться с ней придется весь цикл жизни приложения.

Увы, но однажды приходилось менять ORM. И был случай перехода на NoSQL.

Z>А не выносить — приведет к навигационному доступу и куче однострочников которые используются в одном месте.


что есть навигационный доступ?
читайте о Specification pattern.
Re[4]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 07:31
Оценка:
Здравствуйте, Gengzu, Вы писали:


Z>>Это фантастика. Просто так взять и поменять ORM может получиться только в очень простых приложениях. А поддерживать абстракцию и бороться с ней придется весь цикл жизни приложения.


G>Увы, но однажды приходилось менять ORM. И был случай перехода на NoSQL.


С этого места подробнее. Сколько было сущностей, как абстрагированы и сколько всего пришлось переделать за пределами абстракций.

Z>>А не выносить — приведет к навигационному доступу и куче однострочников которые используются в одном месте.


G>что есть навигационный доступ?


foreach (var orderLine in order.Lines)
  foreach (var good in orderLine.Goods)
  {
    good.Manufacturer.Score++;
  }


G>читайте о Specification pattern.


Он отлично реализован в .net через ExpressionTree. Именно его я и советую применять выставляя наружу IQueryable.
Re[4]: Работа с ORM
От: Aviator  
Дата: 26.07.11 07:34
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Здравствуйте, Аноним, Вы писали:


А>>Да, я это понимаю, но хочется иметь некую абстракцию в коде:


А>>
А>>using (IUnitOfWork uow = UnitOfWork.CreateUow())
А>>{
А>>   uow.BeginTransaction();
А>>   .......
А>>   uow.CommitTransaction();
А>>}
А>>


Z>Что вам даст эта абстракция? На какой ORM вы сможете поменять NHibernate? Какие детали NHibernate она скроет?

Да пусть играется с абстракциями это сейчас модно , главное что бы репозитарии использовали хибер а не непонятные абстракции.
Re[5]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 08:00
Оценка:
Z>С этого места подробнее. Сколько было сущностей, как абстрагированы и сколько всего пришлось переделать за пределами абстракций.

С десяток сущностей. Много отношений много-ко-многим. Изначально использовался EF 4.0, как маппер. После EF 4.1 CodeFirst, в итоге перешли на NHibernate, так как гибкости EF в маппингах сильно не хватало.

G>>что есть навигационный доступ?


Z>
Z>foreach (var orderLine in order.Lines)
Z>  foreach (var good in orderLine.Goods)
Z>  {
Z>    good.Manufacturer.Score++;
Z>  }
Z>


не понимаю к чему это.
изменение Score у вложенной сущности? как бы это выглядело в вашем случае?

Z>Он отлично реализован в .net через ExpressionTree. Именно его я и советую применять выставляя наружу IQueryable.


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

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

но всё сильно от задачь зависит. в HomePage такие абстракции лишние.
Re[6]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 08:16
Оценка:
Здравствуйте, Gengzu, Вы писали:

Z>>С этого места подробнее. Сколько было сущностей, как абстрагированы и сколько всего пришлось переделать за пределами абстракций.


G>С десяток сущностей. Много отношений много-ко-многим. Изначально использовался EF 4.0, как маппер. После EF 4.1 CodeFirst, в итоге перешли на NHibernate, так как гибкости EF в маппингах сильно не хватало.


Я так и не услышал ответа на весь вопрос. Впрочем дам совет, хоть он и не будет воспринят сейчас: не надо напирать на хитрые маппинги, чем они прозрачнее, тем проще разработчику работать с базой. Этот факт все равно не скроешь, хоть 50 абстракций наверни сверху.

G>не понимаю к чему это.

G>изменение Score у вложенной сущности? как бы это выглядело в вашем случае?

Очень и очень вложенной, обычная N+1 проблема навигационного доступа. В гибернейте, в моем случае, это выглядело бы как запрос к нужным сущностям, апдейт и сохранение. Именно таким запросам мешают различные репозитарии вкупе с запретом на IQueryable. В bltoolkit вообще, прямой апдейт в БД.

Z>>Он отлично реализован в .net через ExpressionTree. Именно его я и советую применять выставляя наружу IQueryable.


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


Я рад, что развеял заблуждение:

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


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


Когда получит тогда и надо вводить его. Сто процентов что, введенный сейчас он не ляжет на неожиданно возникшие вебсервисы органично, будут нюансы.

G>но всё сильно от задачь зависит. в HomePage такие абстракции лишние.


Ну мы то сейчас не о HomePage, впрочем в одном моем домашнем проекте сущностей уже больше 10. И логика меняется постоянно. И все абстракции, кроме используемой платформы — выкинуты. Полет нормальный.

P.S. Я сам несколько лет назад любил NH именно за хитрые маппинги. Но тогда не было строготипизированного доступа к БД, который дает LINQ.
Re[7]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 08:32
Оценка:
Z>Я так и не услышал ответа на весь вопрос. Впрочем дам совет, хоть он и не будет воспринят сейчас: не надо напирать на хитрые маппинги, чем они прозрачнее, тем проще разработчику работать с базой. Этот факт все равно не скроешь, хоть 50 абстракций наверни сверху.

Чего конкретно не хватает в ответе?
За пределами сущностей пришлось переделать очень не много, и то скорее потому, что RepositoryХзЧто привелись к нормальному GenericRepository с использованием Specification, что дало возможность выгребать везде одинаково необходимое кол-во необходимых сущностей, включая вложенные, при необходимости.

Ну и не ясно что значит "не надо напирать на хитрые маппинги". Они могут быть продиктованы бизнесс-требованиями, имеющейся базой данных, которая уже есть такая какая есть и её нужно замапить.

Z>Очень и очень вложенной, обычная N+1 проблема навигационного доступа. В гибернейте, в моем случае, это выглядело бы как запрос к нужным сущностям, апдейт и сохранение. Именно таким запросам мешают различные репозитарии вкупе с запретом на IQueryable. В bltoolkit вообще, прямой апдейт в БД.


глупости. сделайте эту сущность Aggregation Root, и получите к ним доступ через репозиторий. не вижу проблем.

Z>Я рад, что развеял заблуждение:


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

Z>Когда получит тогда и надо вводить его. Сто процентов что, введенный сейчас он не ляжет на неожиданно возникшие вебсервисы органично, будут нюансы.


Паттерн Repository по сути является не чем иным, как реализацией массива в памяти с разными заморочками. Если он таким и будет оставатся, то в совокупности с UnitOfWork, привязывание его к веб-сервисам пройдёт прозрачно.
Re[5]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 08:35
Оценка:
A>Да пусть играется с абстракциями это сейчас модно , главное что бы репозитарии использовали хибер а не непонятные абстракции.

Так а репозитории что дают? Кроме возможности поменять ORM? Их же надо писать. И потом проталкивать каждый метод дергания БД в них. Вместо того, чтобы писать Users.Where(u => u.Sex == Sex.Male), приходится писать


IUserRepository.cs:
IList<User> GetUsersBySex(Sex sex);

UserRepository.cs:
IList<User> GetUsersBySex(Sex sex) 
{
   return session.Linq<User>().Where(u => u.Sex == sex).ToList();
}

текущий код:
UserRepository.GetUsersBySex(Sex.Male)


Пишем и радуемся абстракциям. У нас такой клевый метод. Его могут все использовать. Потом приходит аналитик и говорит: там оказывается не мужчины нужны, а мальчики, моложе 14 лет. И что будет делать программер? Его метод уже все используют. Добавить в него фильтр по возрасту — означает поломать чей-то код. Вобщем он либо пишет новый метод GetUsersBySexYongerThat (в лучшем случае), либо (в худшем) в коде делает фильтр по возрасту у результата GetUsersBySex, в результате мы имеем в памяти (и сессии) в несколько раз больше объектов чем нужно. А гибернейт очень нервно относится к количеству объектов в сессии, там производительность то ли O(N) то ли O(N^2). И это падение не происходит за один день. Оно копится милисикундами.

Вобщем я не выдумываю небылицы, это собственный опыт.
Re[6]: Работа с ORM
От: Gengzu  
Дата: 26.07.11 08:46
Оценка:
Z>Пишем и радуемся абстракциям. У нас такой клевый метод. Его могут все использовать. Потом приходит аналитик и говорит: там оказывается не мужчины нужны, а мальчики, моложе 14 лет. И что будет делать программер? Его метод уже все используют. Добавить в него фильтр по возрасту — означает поломать чей-то код.

та же проблема возникает с прямыми запросами. когда нужно выбирать не только мальчиков, вы будете править 100+ мест где написали этот ваш запрос.
Re[8]: Работа с ORM
От: Ziaw Россия  
Дата: 26.07.11 08:49
Оценка:
Здравствуйте, Gengzu, Вы писали:

G>Чего конкретно не хватает в ответе?

G>За пределами сущностей пришлось переделать очень не много, и то скорее потому, что RepositoryХзЧто привелись к нормальному GenericRepository с использованием Specification, что дало возможность выгребать везде одинаково необходимое кол-во необходимых сущностей, включая вложенные, при необходимости.

О! GenericRepository это же чудесно. Вместо того, чтобы писать кучу бесполезного кода, мы его засунем в шаблон и волшебно получим свою порцию бесползеного кода для каждой сущности. Вобщем Specification таки добро оказалось, это радует.

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


То есть, как только я не могу сделать адекватный запрос, я должен вынести Aggregation Root, дать доступ к репозитарию. Ничего, что программисту дают оптимистичные сроки и он не захочет создавать новые репозитарии на простенький запрос? На него же еще и тесты писать придется.

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


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

G>Паттерн Repository по сути является не чем иным, как реализацией массива в памяти с разными заморочками. Если он таким и будет оставатся, то в совокупности с UnitOfWork, привязывание его к веб-сервисам пройдёт прозрачно.


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

Z>>Пишем и радуемся абстракциям. У нас такой клевый метод. Его могут все использовать. Потом приходит аналитик и говорит: там оказывается не мужчины нужны, а мальчики, моложе 14 лет. И что будет делать программер? Его метод уже все используют. Добавить в него фильтр по возрасту — означает поломать чей-то код.


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


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

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

Z>То есть, как только я не могу сделать адекватный запрос, я должен вынести Aggregation Root, дать доступ к репозитарию. Ничего, что программисту дают оптимистичные сроки и он не захочет создавать новые репозитарии на простенький запрос? На него же еще и тесты писать придется.


опять глупость. один GenericRepository обслуживает ВСЕ сущности в проекте. один класс с десятком методов по 1-3 строки каждый, пишется не так долго, правда?)

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


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


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


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