Dec 23, 2009

Проблема наследования в Nhibernate

Для меня данный вопрос остаётся открытым вот уже год. В обещанном примере приложения есть следующая иерархия:

Диаграмма классов с наследованием

Схема базы будет сгенерирована автоматически, если убрать комментарий со строки в Global.asax (ExposeConfiguration(BuildSchema)).

Код, который пытается изменить тип контента может выглядеть так:

var photo = Global.CurrentSession.Get<Photo>(2);
var video = new Video();
video.FromPhoto(photo);

Global.CurrentSession.Evict(photo);

Global.CurrentSession.SaveOrUpdateCopy(video);

Где метод FromPhoto выглядит так:

public virtual void FromPhoto(Photo photo)
{
    this.Id = photo.Id;
    this.Name = photo.Name;
    this.UploadDate = photo.UploadDate;
}

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

Исходный код

25 comments:

  1. Ты бы на Хабре запостил :) Может найдутся ответы

    ReplyDelete
  2. Видимо предполагается, что это нелогично. Почему это фото должно становится видео? Логичнее сделать новый объект "видео", куда передать фото и настроить нужные связи и т.п. А тут ситуация, когда нужен апгрейд одной сущьности до совсем другой.

    ReplyDelete
  3. Ну это просто демка, я же в прошлом посте писал когда это реально нужно

    ReplyDelete
  4. Давай сюда реальный пример, на нём и будем разбираться =)

    ReplyDelete
  5. http://stackoverflow.com/questions/594187/nhibernate-inheritance-problem-when-saving-childs

    ReplyDelete
  6. Ну я думаю там исчерпывающий ответ, особенно вот с этим я согласен:
    "If you are in a position to change your design, I suggest you do that - generally, you shouldn't use inheritance when the type of your specialization could change. It's much better and more convenient to introduce some kind of Role in your domain."

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

    ReplyDelete
  7. дизайн условно плохой. И обнаружить эту плохость можно только непосредственно наткнувшись. Тем более что вопрос решается через SQL апдейты. Хотелось бы решение через ORM.

    ReplyDelete
  8. Проблема вытекает из того, что нужно сохранить его айди, а зачем это делать? Добавляем новую запись нужного типа, копируем в неё всё, что связано с больше нам ненужной и грохаем ненужную. Да это небольшой overhead, но далеко не всегда можно заставить ORM выполнять минимальный скл код, примеров масса. Придётся делать выбор или минимальный скл вызов или "оэремность".

    ReplyDelete
  9. Переписывание всех свойств, которые надо сохранить дает гору проблем. Опять же добавление одного нового свойства в Photo обязывает не забывать добавлять его переписывание, а это 100% забудут сделать.

    ReplyDelete
  10. > Проблема вытекает из того, что нужно сохранить
    > его айди, а зачем это делать?

    слай написал - чтоб ссылки на него остались рабочими.

    но моё имхо такое, что на старые ссылки надо отдавать 301 Moved Permanently и новый линк. на новую сущьность. т.е. проблему решить можно чуть в другой плоскости.

    ReplyDelete
  11. Да, вопрос с сохранением линки вполне решаем редиректом. Но не решаются вопросы каскадного удаления.

    ReplyDelete
  12. а при чём тут каскадное удаление? после передвижения каментов и прочего в новую сущьность, ничего уже удалять не надо.

    ReplyDelete
  13. Ну я имею ввиду что может быть очень большой граф связанных объектов. И их тупо забудут перевесить на новый объект

    ReplyDelete
  14. Хороший вопрос... В моем текущем проекте возникла данная проблема. Есть некоторая сущность Company, у неё есть 2 наследника TargetCompany и PlacementCompany. При создании новой компании обязательно нужно указать её тип. Т.к. набор свойств у обеих компаний абсолютно одинаковый(единственное различие это сам тип), то в БД была создана таблица Company c полем CompanyType. В проекте был реализован класс Company и два его наследника TargetCompany и PlacementCompany. В маппингах Company было указано что поле CompanyType в БД является дискриминатором для наследников. Но в определенный момент встал вопрос о редактировании компании, и заказчику очень захотелось изменять тип Company на определенных условиях. Company имеет кучу связанных сущностей: Tasks, Events, Positions, Contacts - поэтому в лоб создать новую компанию другого типа, но со всеми параметрами старой компании есть очень трудоемкое и не очень красивое решение. На данный момент был временно подставлен костыль в ввиде SQL запроса который меняет тип компании, но вопрос остается вполне актуальным...

    ReplyDelete
  15. 2 Sly
    кто забудет? если у тебя есть что-то типа
    Video video = photo.ToVideo();

    то там самое место иметь весь код по муву связанных объектов.

    но если брать конкретно твой пример, то, конечно, надо просто иметь другую структуру классов. а-ля:

    ViewableAndCommentableEntity, с которой будут связаны каменты и на которую будут линки. а эта энтити будет включать в себя как поле content, который может быть видео, аудио или вообще статья (текст).

    ReplyDelete
  16. 2 максим

    то же самое. компания должна быть полем у объектов типа Target и Placement.

    ведь это логично.

    ReplyDelete
  17. Пришли к тому, с чего начинали. Что использовать композицию вместо наследования тут можно - это не вопрос. Но я не вижу принципиальной не правильности в попытке подобной смены типа. Если у нас есть студент и преподаватель. Оба они наследуют тип Human. Но студент, закончивший ВУЗ, может стать преподавателем, и при этом по прежнему наследовать Human. Везде полноценные связи "is a", почему же это не наследование?

    ReplyDelete
  18. потому что в общем случае преподаватель может быть не только человеком, но и компьютерной системой.

    вообще надо смотреть на домен. честно говоря, мало где надо иметь настолько глобальные иерархии - от "человека".

    общие поля у препода и студента скорее всего заключены в "размеры", "адрес", "биология (тип крови)" - в других под-классах.

    кстати я всё чаще и чаще ловлю себя на мысли, что реальные бизнес-задачи редко требуют иерахий наследования.
    а вот чисто компьютерные (вспомогательные, типа разработка ИДЕ, Редакторов) наоборот требуют.

    такие дела.

    ReplyDelete
  19. Может я не в тему.
    Но Харьковчанам респект.

    ReplyDelete
  20. 2 СОТОНА:
    >кстати я всё чаще и чаще ловлю себя на мысли, что >реальные бизнес-задачи редко требуют иерахий >наследования.
    Аналогично, меня это пугало, оказывается зря =)
    2 Sly:
    Композиция это отлично, наследование замечательно =) В конкретной ситуации действительно композиция решает проблему, но как я понимаю вопрос стоит чисто теоретически "а вот как сделать такую штуку в nHibernate", т.е. здесь скорее сыграла своё пытливость ума и так сказать geek-нутость =) Я прав?

    ReplyDelete
  21. Чем мне не нравится композиция - она не позволяет избегать операторов if. Именно из-за этих причин я предпочитаю почаще использовать именно схему наследования. Как правило проблем с этим не возникает, ну кроме описанной тут :)

    ReplyDelete
  22. >она не позволяет избегать операторов if.
    да прямо таки.

    типа из-за композиции полиморфизм отменили :)

    если чё, то вместо
    foreach (с in content) {
    c.show()
    с.showComments()
    }

    у тебя будет
    foreach (с in content) {
    c.content.show()
    c.showComments()
    }

    content - колекция фоток, статей, видео и прочего.

    пример притянут зауши, просто чтобы показать, что никаких if'ов. :)

    ReplyDelete
  23. c.content - поле, для нашей композиции, которое уже есть или видео или фотка или статья.

    ReplyDelete
  24. Или я чего то не понимаю, или для поля content тоже нужна будет иерархия наследования?

    ReplyDelete
  25. ну достаточно реализовывать общий интрефейс :)

    но суть же не в том, что совсем не надо наследования (хотя в данном случае можно и без него), а в том, что композиция вполне работает и цепочки if'ов или switch'и не нужны.

    а самая уже суть, конечно, в том, что у тебя для связи объектов есть связующий класс, который служит именно для связи. а не "для хранения контента" и "для хранения коментов к контенту". типа, single responsibility principle...

    ReplyDelete