Отражение строк базы на разные объекты домена

52 views
Skip to first unread message

Андрей Чистяков

unread,
Mar 14, 2012, 2:58:54 AM3/14/12
to dotnetconf
Доброго времени суток,

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

Приложение делает запросы к БД, результаты запросов преобразуются в
классы предметной области (в качестве ORM использую NHibernate) и
показываются пользователю в некоторых представлениях (какие-то
контролы WinForms). Допускается, что пользователь видит сразу
результаты нескольких разных запросов в отдельных представлениях
(например, чтоб сравнить результаты поиска по разным критериям).

Итак, предположим мы выполняем запрос, он среди прочего возвращает
строку с ID=42. Эта строка маппится на объект A, объект показывается в
соответствующем представлении. Далее выполняем другой запрос, он также
возвращает строку с ID=42. Поскольку второй запрос выполняется позже и
в другой сессии, эта строка маппится на другой объект B; объект В
показывается в другом представлении. С точки зрения database identity
объекты А и В - это одно и то же, т.к. у них одинаковый ID; с точки
зрения .NET - это разные объекты по разным ссылкам (или точнее разные
копии одного объекта).

Самое интересное начинается, когда пользователь начинает редактировать
(через представление) объект А. Отредактировал, сохранил в базу. Как
теперь быть с объектом В? нужно же значения полей синхронизировать с
объектом А, чтобы во втором представлении тоже показывались актуальные
данные. Я пока использую паттерн Observer: 1) отредактировали объект
2) отправили уведомление "Объект с ID=42 изменился, поменялись такие-
то поля" 3) заинтересованные объекты модели ловят это уведомление,
обновляют свои копии объектов с ID=42, если они у них имеются и 4)
уведомляем представления, для которых были изменения в соответсвующих
объектах домена, чтоб перерисовались.

Правильное ли я выбрал решение проблемы? Как принято поступать в
подобных случаях?

Alexey Romanovsky

unread,
Mar 14, 2012, 5:22:09 AM3/14/12
to dotne...@googlegroups.com
Очень рекомендую прочитать (если это еще не прочитано) статью Создание настольного приложения с применением NHibernate из MSDN Magazine. В статье как раз обсуждается аналогичная проблема и рассматриваются две крайности при работе с сессиями в NHibernate. И, кстати говоря, предлагается аналогичное решение.

Но лично я при разработке настольных приложений с NHibernate поступаю следующим образом:
1) во всех представлениях использую вьюмодельки (простые DTO) вместо объектов домена (соответственно, выбираю одну из крайностей, описанных в статье, т.е. открываю сессию только на время выполнения бизнес-операции)
2) после редактирования какой-то сущности тупо обновляю представления (в частности, списки, гриды и т.д.), то есть особо не заморачиваюсь на то, чтобы найти только нужные объекты и обновить их.

Вот так.

среда, 14 марта 2012 г. 12:58:54 UTC+6 пользователь Андрей Чистяков написал:
среда, 14 марта 2012 г. 12:58:54 UTC+6 пользователь Андрей Чистяков написал:
среда, 14 марта 2012 г. 12:58:54 UTC+6 пользователь Андрей Чистяков написал:
среда, 14 марта 2012 г. 12:58:54 UTC+6 пользователь Андрей Чистяков написал:
среда, 14 марта 2012 г. 12:58:54 UTC+6 пользователь Андрей Чистяков написал:

Евгений Сверчков

unread,
Mar 14, 2012, 9:24:19 AM3/14/12
to dotnetconf
Не пользовался NHibernate, но могу предположить, что при создании
контекста подключения можно использовать патерн Singleton. Если
объект из базы был загружен, то контекст вернет ссылку на загруженный
объект, таких проблем у тебя не будет.

Андрей Чистяков

unread,
Mar 14, 2012, 2:06:12 PM3/14/12
to dotnetconf
On 14 мар, 12:22, Alexey Romanovsky <alogic...@gmail.com> wrote:
> Очень рекомендую прочитать (если это еще не прочитано) статью
Огромное спасибо за ссылку! Эта статья как раз очень подходит к моей
ситуации, так что я убедился, что иду примерно верным путем, плюс
почерпнул ряд новых полезных идей.

> Но лично я при разработке настольных приложений с NHibernate поступаю
> следующим образом:
> 1) во всех представлениях использую вьюмодельки (простые DTO) вместо
> объектов домена (соответственно, выбираю одну из крайностей, описанных в
> статье, т.е. открываю сессию только на время выполнения бизнес-операции)
> 2) после редактирования какой-то сущности тупо обновляю представления (в
> частности, списки, гриды и т.д.), то есть особо не заморачиваюсь на то,
> чтобы найти только нужные объекты и обновить их.

Попробую Ваш подход, вероятно, он поможет мне упростить некоторые
вьюхи, которые у меня в основном read-only.

Еще раз спасибо, Вы мне очень помогли!

Андрей Чистяков

unread,
Mar 14, 2012, 2:12:28 PM3/14/12
to dotnetconf
On 14 мар, 16:24, Евгений Сверчков <u02...@gmail.com> wrote:
> Не пользовался NHibernate, но могу предположить, что при создании
> контекста подключения можно использовать  патерн Singleton. Если
> объект из базы был загружен, то контекст вернет ссылку на загруженный
> объект, таких проблем у тебя не будет.

Да нет, singleton мне как раз не нужен, т.к. он приводит к другим
проблемам, в том числе и описанным в упомянутой Алексеем Романовским
(извиняюсь, если неверно траслителировал фамилию) статье.

ShuriK

unread,
Mar 15, 2012, 4:40:33 AM3/15/12
to dotne...@googlegroups.com
Singleton для объектов отображения имхо уж совсем жестоко.
Ваш вариант вполне логичный, только я бы, не рассылал, а регистрировал измененные Id. далее при активизации  представления смотрим список изменений с момента X и, при необходимости, рефрешимся из базы.
бонусы - нет дублирования логики (если поменялось поле тут то поле надо проставить там), работает на абсолютно различных по содержанию представлениях(не всегда можно просто заменить измененное поле), не нужны события на все изменения.
минусы - трип к базе для обновления представлений, необходимость заставить NH обновить измененный объект из базы а не взять старый из кэша.

Андрей Чистяков

unread,
Mar 15, 2012, 5:23:00 AM3/15/12
to dotnetconf
On 15 мар, 11:40, ShuriK <alex.ziz...@gmail.com> wrote:
> Singleton для объектов отображения имхо уж совсем жестоко.
> Ваш вариант вполне логичный, только я бы, не рассылал, а регистрировал
> измененные Id. далее при активизации  представления смотрим список
> изменений с момента X и, при необходимости, рефрешимся из базы.
> бонусы - нет дублирования логики (если поменялось поле тут то поле надо
> проставить там), работает на абсолютно различных по содержанию
> представлениях(не всегда можно просто заменить измененное поле), не нужны
> события на все изменения.
> минусы - трип к базе для обновления представлений, необходимость заставить
> NH обновить измененный объект из базы а не взять старый из кэша.
>
Интересное решение, мне такое не приходило в голову. Сейчас мне сходу
трудно оценить, как это будет выглядеть в коде, попробую реализовать -
возможно, пригодится. Спасибо.

Alexander Byndyu

unread,
Mar 16, 2012, 4:40:42 AM3/16/12
to dotne...@googlegroups.com
Добрый день, Андрей!

во всех представлениях использую вьюмодельки (простые DTO) вместо объектов домена  

Поддержу идею Андрей :) На View должны попадать только DTO. Если вы используете MVP, то это будут сформированные модели (M). Работа с доменными объектами на View, особенно при использовании ORM сильно свяжет данные и представление.

Возможно вам пригодится шаблон http://martinfowler.com/eaaCatalog/identityMap.html, хотя более логично выглядит идея с событиями во View.

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

--
Best regards,
Byndyu Alexander
Director at IndyCode – www.indycode.ru

Phone: +7 (904) 305 5263
Skype: alexander.byndyu
Blog: http://blog.byndyu.ru
Twitter: alexanderbyndyu



15 марта 2012 г. 15:23 пользователь Андрей Чистяков <ogi...@mail.ru> написал:

Андрей Чистяков

unread,
Mar 16, 2012, 11:40:16 AM3/16/12
to dotnetconf
On 16 мар, 11:40, Alexander Byndyu <alexander.byn...@gmail.com> wrote:

Добрый день, Александр,

> Андрей, а что заставило вас усомниться в выбранном решении? Какие проблемы> начали у вас возникать?Как таковых проблем, которые бы можно было назвать, нет. Просто я не так давно начал изучать DDD, поэтому у меня еще не выработался свой стиль и багаж проверенных решений, которые можно применять не задумываясь (не очень нравится это выражение, думать в любом случае нужно), а просто основываясь на своем опыте. Поэтому у меня сейчас пока идет этап обучения, проб и ошибок, так что хотелось подстраховаться, убедиться, что решение, которое я принял и которое в итоге пойдет в боевой продукт, верное.

> Поддержу идею Андрей :) На View должны попадать только DTO. Если вы
> используете MVP, то это будут сформированные модели (M). Работа с доменными
> объектами на View, особенно при использовании ORM сильно свяжет данные и
> представление.

А Вы правы, так и есть, уже связало. Я только сейчас об этом
задумался ) И еще я попробовал представить, как у меня будут выглядеть
эти DTO, и получается, что они не особо будут отличаться от собственно
объектов домена. А это говорит о том, что объекты домена у меня
используются по сути только для хранения данных, т.е., видимо, у меня
в проекте получилось то, что называется анемичной доменной моделью, я
прав? Похоже, придется мне пересмотреть свою доменную модель :)

Спасибо за Ваш комментарий!

Андрей Чистяков

unread,
Mar 16, 2012, 11:47:13 AM3/16/12
to dotnetconf
On 16 мар, 18:40, Андрей Чистяков <ogio...@mail.ru> wrote:

Что-то я с форматированием текста в предыдущем сообщении напутал,
извиняюсь (

Alexander I. Zaytsev

unread,
Mar 16, 2012, 12:44:05 PM3/16/12
to dotne...@googlegroups.com
Нормально;)

16 марта 2012 г. 21:47 пользователь Андрей Чистяков <ogi...@mail.ru> написал:

Alexander I. Zaytsev

unread,
Mar 16, 2012, 1:01:23 PM3/16/12
to dotne...@googlegroups.com
inline

16 марта 2012 г. 21:40 пользователь Андрей Чистяков <ogi...@mail.ru> написал:

On 16 мар, 11:40, Alexander Byndyu <alexander.byn...@gmail.com> wrote:

Добрый день, Александр,

> Андрей, а что заставило вас усомниться в выбранном решении? Какие проблемы> начали у вас возникать?Как таковых проблем, которые бы можно было назвать, нет. Просто я не так давно начал изучать DDD, поэтому у меня еще не выработался свой стиль и багаж проверенных решений, которые можно применять не задумываясь (не очень нравится это выражение, думать в любом случае нужно), а просто основываясь на своем опыте. Поэтому у меня сейчас пока идет этап обучения, проб и ошибок, так что хотелось подстраховаться, убедиться, что
 
решение, которое я принял и которое в итоге пойдет в боевой продукт, верное.
Нет ничего плохого в том, что решение будет не идеальное. "Лучшее враг хорошего"
 
 
> Поддержу идею Андрей :) На View должны попадать только DTO. Если вы
> используете MVP, то это будут сформированные модели (M). Работа с доменными
> объектами на View, особенно при использовании ORM сильно свяжет данные и
> представление.
А Вы правы, так и есть, уже связало. Я только сейчас об этом
задумался ) И еще я попробовал представить, как у меня будут выглядеть
эти DTO, и получается, что они не особо будут отличаться от собственно
объектов домена. А это говорит о том, что объекты домена у меня
используются по сути только для хранения данных, т.е., видимо, у меня
в проекте получилось то, что называется анемичной доменной моделью, я
прав? Похоже, придется мне пересмотреть свою доменную модель :)
Да, вы правы. Если они совершенно идентичны, то это ни что иное как анемичная модель, или простое CRUD приложение. 
Но в этом нет ничего плохого, не нужно лепить DDD во все места, а как любой инструмент применять разумно. Молотком тоже можно шурупы забивать, но не очень эффективно.

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

Но в любом из этих вариантов использование ViewModel/DTO/etc (называйте как хотите) дает большой плюс в изоляции отображения от данных: можно независимо менять интерфейс и структуру нижележащих данных (такое требование очень часто встречается). Можно даже поменять источник данных и т.д.

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

Нужно придерживаться правила - одна ViewModel на одно отбражение. Во ViewModel должно быть только то, что используется в отображении. 

Как делаю я (правда вёб разработка, но когда пишу настольные приложения использую тот же подход за счет использования тонкого клиента):

1. создаю пустой класс
2. объявляю что он у меня модель для этого представления.  
3. в представлении работаю с несуществующими полями. 
4. создаю поля, которые понадобились. 
5. пишу мапинг из сущностей (как автомаппер, так и вручную, в зависимости от настроения или практик, принятых в текущем проекте).

При таком подходе я получаю следующие плюсы:
1. можно изменять разные представления полностью независимо.
2. модель домена и модель отображения можно менять независимо.

Минус - возрастает количество классов, но это не беда, если использовать понятные соглашения именования.

 
 
Спасибо за Ваш комментарий!


Андрей Чистяков

unread,
Mar 16, 2012, 2:21:02 PM3/16/12
to dotnetconf
On 16 мар, 20:01, "Alexander I. Zaytsev" <hazzik+nos...@gmail.com>
wrote:
> inline

> Нет ничего плохого в том, что решение будет не идеальное. "Лучшее враг
> хорошего"

золотые слова :)

> Да, вы правы. Если они совершенно идентичны, то это ни что иное как
> анемичная модель, или простое CRUD приложение.
> Но в этом нет ничего плохого, не нужно лепить DDD во все места, а как любой
> инструмент применять разумно. Молотком тоже можно шурупы забивать, но не
> очень эффективно.

Да, пока у меня видимо эйфория от мощи и возможностей DDD не прошла,
подозреваю, что использую его где надо и где не надо. Пройдет )

> Как делаю я (правда вёб разработка, но когда пишу настольные приложения
> использую тот же подход за счет использования тонкого клиента):
>
> 1. создаю пустой класс
> 2. объявляю что он у меня модель для этого представления.
> 3. в представлении работаю с несуществующими полями.
> 4. создаю поля, которые понадобились.
> 5. пишу мапинг из сущностей (как автомаппер, так и вручную, в зависимости
> от настроения или практик, принятых в текущем проекте).
>
> При таком подходе я получаю следующие плюсы:
> 1. можно изменять разные представления полностью независимо.
> 2. модель домена и модель отображения можно менять независимо.
>
> Минус - возрастает количество классов, но это не беда, если использовать
> понятные соглашения именования.

Я правильно понимаю, что в итоге имеем как маппинги для DTO, так и для
объектов домена, причем первые используются в основном в режиме read-
only (прочитали из базы, сформировали DTO, показали во вьюхе), а
вторые - read/write?

Reply all
Reply to author
Forward
0 new messages