LLBLGen vs. NHibernate

18 декабря 2009 г.

Хотел сделать сравнение двух ORM ответом на пост «Встреча казанской UserGroup 9.12.2009». Выписал все плюсы и минусы в таблицу. Взвесил все «за» и «против» и сделал вывод, какая ORM все-таки лучше.

Потом все это удалил, потому что в таком сравнении нет смысла. Во-первых, уйма народу это уже сделали. Во-вторых, лучшей ORM быть не может, т.к. в каждом проекте свои нюансы и их надо учитывать. Вместо этого я напишу причины, из-за которых мы отказались от LLBLGen и перешли на NHibernate.

Сильные недостатки
Бинарный .lgp

LLBLGen использует редактор с графическим интерфейсом для задания маппингов. Этот редактор создает свой файл проекта (файл с расширением .lgp), в котором хранит все настройки. По неудачному стечению обстоятельств этот файл бинарный. Системы контроля версий не умеют сливать бинарные файлы. Поэтому, если вам надо изменить какую-то настройку в маппинге, придется предупредить всю команду, что вы забираете файл .lgp на редактирование. Когда проектом занималось 2 человека, это не вызывало проблем. Но когда в проекте участвует 5 программистов, это становится реальным ограничением. Приходится ждать, пока файл освободится.

Медленное считывание/записывание свойств

После написания одного из консольных приложений для нашего проекта, я начал оптимизировать его по скорости. Каково было мое удивление, когда в профайлере я увидел, что 30% времени съедает вот такая строчка кода:

   1:  return product.Price > 0;

Обычный вызов get-функции для decimal-значения. Что тут может тормозить? Смотрю код, который сгенерировал LLBLGen для сущности Product:

   1:  public virtual System.Decimal Price
   2:  {
   3:      get { return (System.Decimal)GetValue((int)ProductFieldIndex.Price, true); }
   4:      set { SetValue((int)ProductFieldIndex.Price, value); }
   5:  }

Вместо того, чтобы просто вернуть значение переменной, происходят какие-то шаманские действия. Остальные get/set-функции в свойствах всех сгенерированных объектов выглядят точно также.

Сложное внутренне устройство сущностей

Сгенерированный класс с 10 полями в базе данных и двумя связанными коллекциями обычно содержит 700-900 строк кода. Все это нужно только для того, чтобы LLBLGen мог нормально работать.

К сожалению, особенности внутреннего устройства LLBLGen постепенно переползают за сгенерированный проект модели. Например, вы изменили поле в сущности Product. Теперь, чтобы узнать изменилось ли поле, надо обратиться к свойству IsDirty. Зачем рефлексия, говорят нам на форуме LLBLGen, используйте список Fields. Он содержит все поля сущности с их названиями, текущими и измененными значениями. В конце концов в коде, то там то тут, вылезают различные особенности устройства LLBLGen сущностей.

И это плохо, потому что весь наш проект узнал про ORM.

В добавок к этому приведу список публичных свойств/методов, которые доступны для сгенерированной сущности:

Этот длинный список вообще никакой смысловой нагрузки не несет. Чтобы просто найти свойства класса Product придется потратить изрядно времени.

А если вы захотите передать такой объект сериализованным в JSON, то он придет с полями:

  • CustomProperties
  • FieldsCustomProperties
  • IsDirty
  • IsNew
  • ...

Клиенту, который принимает JSON эти данные не нужны. Все эти публичные поля нужны только для внутренней работы LLBLGen.

Неприятные мелочи

Кроме этого, есть недостатки, с которыми можно было бы продолжать работать. Тем не менее они вызывают раздражение.

Не просто ORM

LLBLGen – это не просто ORM, это целый слой доступа к данным. Вот как они позиционируют свою утилиту:

«LLBLGen Pro generates a complete data-access tier and business objects tier for you.»

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

Тонна кода

Последние подсчеты NDepend показали, что 2/3 нашего проекта состоит из кода сгенерированного LLBLGen.

Вместо LLBLGen

После 1,5 лет использования LLBLGen мы решили перейти на NHibernate. Одна из самых банальная причин перехода – у него нет недостатков LLBLGen'а:

  • Маппинги хранятся прямо в коде. Изменять маппинги можно без проблем всей командой, т.к. это текстовые файлы, они очень просто сливаются системой контроля версий. К счастью, теперь можно задавать маппинги не через XML, а прямо на языке C#;
  • NHibernate использует «чистые» доменные объекты. Соответственно, обращаться к полю через get-функцию ничего не стоит;
  • Опять же «чистые» доменные объекты очень хорошо сериализуются и имеют простой набор публичных свойств/методов.

Плюс ко всему у NHibernate есть очень удобный профайлер. Рекомендую!


Дополнение 1

Мне написал Frans Bouma, создатель LLBLGen. Франс перевел этот пост и написал мне письмо. Он заверил, что в 3-ей версии его ORM проблем, описанных мною, уже не будет. Могу только пожелать ему удачи! Ну, а как 3-ая версия выйдет, там и посмотрим ;)

Дополнение 2

Наш архитектор написал свои мысли по этому поводу.

37 комментариев:

  1. Мда! С генераторами работать удобно и быстро, но когда заглядываешь под капот, то просто ужОсаешься. У меня так было с Linq2Sql. Пока не забросил идею пользовать генератор от МС, а писать руками маппинг. И проблема с бинарным содержимым тоже была, но в другой области - довелось работать с CrystalReport. А там этот "зверь" все генерит в бины, и ессно дифференс увидеть не получается.
    Хотелось бы теперь увидеть новые статьи по NHibernate в твоем блоге.
    Успехов!

    ОтветитьУдалить
  2. Вопрос на засыпку, а почему нельзя было реализовать доступ к данным ручками? И какого типа ваше приложение? На мой взгляд NHibernete имеет низкую производительность

    ОтветитьУдалить
  3. @DenDL
    > Хотелось бы теперь увидеть новые статьи по NHibernate в твоем блоге.
    Если что интересное под руку попадется, обязательно напишу.

    ОтветитьУдалить
  4. @Nimnul
    > почему нельзя было реализовать доступ к данным ручками?
    Мы экономим свое время. Я считаю, что реализация очередной ORM (читай, очередного велосипеда с квадратными колесами) специально под свой проект - это пустая трата сил.

    > И какого типа ваше приложение?
    Проект состоит из разных приложений. В нем есть сайт, консольные приложения, WinForms, WCF-сервисы

    > На мой взгляд NHibernete имеет низкую производительность
    Здесь нужна ссылка на тесты производительности, иначе это утверждение не имеет смысла.
    В любом случае, пока никаких проблем с производительностью замечено не было.

    ОтветитьУдалить
  5. При выборе ORM инструмента для нового проекта отказались от LLBLGen в пользу Fluent NHibernate по тем же причинам, хотя оно и обеспечивает много готовой функциональности. Лучше дольше разрабатывать и потом продукт будет быстро бегать, чем наоборот.

    ОтветитьУдалить
  6. @eye-ru
    > Лучше дольше разрабатывать и потом продукт будет быстро бегать, чем наоборот
    Согласен, основные расходы обычно идут на поддержку и расширение системы. Быстрый старт далеко не всегда является залогом успешности проекта.

    ОтветитьУдалить
  7. а где же плюсы LLBLGen и недостатки NHibernate? ;)

    ОтветитьУдалить
  8. @hazzik
    Я в начале дал довольно много ссылок на статьи с их сравнениями. Целью статьи является показать причины отказа от LLBLGen.

    Саня, напиши у себя какие плюсы ты видишь в LLBLGen и какие минусы у NHibernate!

    ОтветитьУдалить
  9. "Быстрый старт далеко не всегда является залогом успешности проекта." - вот с этим полностью с тобой согласен. Точно подметил.

    ОтветитьУдалить
  10. Александр, а рассматривали ли возможность применения Entity Framework ?
    Если да, то чем же он хуже NHibernate в вашем случае?
    Просто интересно мнение...

    ОтветитьУдалить
  11. @Tester
    Спасибо за вопрос!

    Про первые 3 версии EF я могу точно сказать, что они на не устраивают по многим причинам. Вот 4ая версия, уже подает надежды на зрелую ORM, которую можно было бы применить в проекте.

    Во-первых EF еще не выпущена. Во-вторых для нее нужен .NET 4 и новая Visual Studio. Это при том, что для нас она ничем не лучше NHibernate.

    Т.е. в перспективе возможно мы будем использовать EF, но для этого должны быть довольно весомые аргументы.

    Если вы применяете EF, то было бы очень интересно увидеть сравнение этой ORM с NHibernate.

    ОтветитьУдалить
  12. Ну если у вас есть много времени, что бы разбираться с глюками ORM то пожалуйста. Нащет производительности, сделай простой тест получения случайных(по коду) данных в десяти потоках и все увидите.

    ОтветитьУдалить
  13. @Nimnul
    > Ну если у вас есть много времени, что бы разбираться с глюками ORM то пожалуйста
    Это ваша интерпретация каких-то конкретных проблем. Опишите их пожалуйста.

    > сделай простой тест получения случайных(по коду) данных в десяти потоках и все увидите.
    Если честно, то не понятно, зачем в 10ти потоках получать случайные данные. В любом случае вы можете прислать код, который медленно/нестабильно/неправильно работает в NHibernate. Я думаю будет интересно выяснить причины такой работы, возможно написать об этом разработчикам.

    ОтветитьУдалить
  14. Александр, спасибо за сравнение.

    Что касается производительности NH, L2S, EF и прочих ORM, то есть довольно много тестов и сравнений. Есть даже сайт ormbattle.net, на котором есть куча синтетических тестов для сравнения различных ORM, в том числе и в отношении производительности. В то же время ormbattle.net часто ругают, особенно разработчики NHibernate во главе с Айендой :)

    Я тоже какое-то время назад баловался подобным сравнением и могу сказать, что NH и EF (v1) проигрывают L2S, но в то же время исходя из моих субъективных тестов NH работает немного быстрее EF (хотя тесты на ormbattle.net говорят немного другое). Вот мои результаты (не стоит считать, что это истина в последней инстанции, но общую картину понять можно):

    http://merle-amber.blogspot.com/2008/11/net-orm-1.html
    http://merle-amber.blogspot.com/2008/11/net-orm-2-sql.html

    Коммент очень большой, разбил на два. Сорри :(

    ОтветитьУдалить
  15. Продолжение:

    Но не нужно забывать, что:

    а) синтетические тесты имеют мало общего с реальностью, поэтому не стоит им сильно доверять,
    б) каждый ORM имеет свои способы оптимизации, которые часто дают ощутимый выигрыш, они есть и у NH, и у L2S, EF, и других ORM,
    в) ORM создавались для решения других задач, а не задачи производительности... поэтому ORM будут _всегда_ медленнее ручного доступа к данным - это плата за удобство, простоту и скорость разработки, и вы сами решаете, готовы ли вы платить эту плату,
    г) из предыдущего пункта следует, что некоторые задачи и даже типы приложений, требующие быстрой скорости выполнения, не следует решать/создавать при помощи ORM, полностью или частично, нужно всегда думать головой.

    Кстати, очень часто программисты сами отстреливают себе ногу при работе с ORM, когда пишут неоптимальный с точки зрения ORM код. К сожалению, у меня нет способов оптимизации NH и LLBL, но по L2S и EF некоторые заметки писал, может, будут полезны и при работе с другими ORM, так как многие принципы работы у них схожи:

    http://merle-amber.blogspot.com/2009/01/linq-to-sql-entity-framework.html

    ОтветитьУдалить
  16. @Александр Кондуфоров
    > из предыдущего пункта следует, что некоторые задачи и даже типы приложений, требующие быстрой скорости выполнения, не следует решать/создавать при помощи ORM, полностью или частично, нужно всегда думать головой.

    Да-да-да! Если вы занимаетесь бухгалтерией, то не надо писать свою CMS, если вы занимаетесь разработкой сайтов, то не надо писать свою 1С.

    @Nimnul, как раз такие ситуации я и называл изобретением велосипеда с квадратными колесами.

    Александр, спасибо за ссылки!

    ОтветитьУдалить
  17. Спасибо за ответ.... :)

    > Если вы применяете EF, то было бы очень интересно увидеть сравнение этой ORM с NHibernate.

    Нет, я EF не применял... потому и интересно... когда мне нужно было сделать выбор в ORM - EF была еще в бете. И, среди бесплатных выбрал SOODA - её автор сейчас работает в MS над EF и, думаю, Lazy Loading в EF - это его заслуга.

    SOODA меня устраивала, да и пока устраивает в текущих проектах, но есть некоторые моменты, из-за которых я поглядываю на другие ORM, но пока явного кандидата на замену не вижу....

    ОтветитьУдалить
  18. @Tester
    Возможно вам понравится NHibernate. На данный момент это уже зрелый продукт, который можно без опасений использовать в своих приложениях.

    ОтветитьУдалить
  19. Что меня убивает так это то что нужно прописывать sqlServerCatalogNameOverwrites чтобы база не слетала... а это значить что хостить несколько баз в одном конфиге проблематично... если только не использовать для других баз другие ORM.

    ОтветитьУдалить
  20. Использую LLBLGEN уже 7 лет и указанные "недостатки" не мешали работать.
    Про тормознутость первый раз слышу. Хотя согласен, если архитектура базируется на чистых доменных объектах то LLBLGEN для нее не подходит. Но не доменом единым жив человек :)

    Может я видел кривое применение NHibernate но извините меня, когда нужно было реализовать сложный фильтр на основе критериев введенных через GUI то в nHibernate коде я увидел запросы к базе практически hardcoded. Это не то что плохо, это ужасно.
    Я спросил разработчика - почему так, он сказал что NHibernate бессилен в таких случаях.
    LLBGEN в этом отношении имеет свой объектный Predicate механизм который позволяет реализовывать динамическую фильтрацию данных при выборке из базы.
    Да и LINQ начиная с версии 2.6 поддерживает.

    ОтветитьУдалить
  21. sqlServerCatalogNameOverwrites существует только в SelfServicing. В случае с Adapter пишете свой CustomDataAdapter наследовавшись от DataAccessAdapter где считывайте Connection String откуда и как захотите.

    ОтветитьУдалить
  22. @Dmitri Demyanik
    Я только рад, если LLBLGen подходит для ваших проектов.

    NHibernate тоже не стоит на месте. Попробуйте новые версии. Если вы последний раз использовали NH год назад, то доработки вас очень порадуют.

    ОтветитьУдалить
  23. Александр,
    все гораздо хуже, пользовался NH 5 лет назад! :)

    Хотя глядя на эту статью вижу что действительно есть движение вперед
    http://slynetblog.blogspot.com/2009/11/nhibernate-4-criteria-queries.html
    (Смущает правда необходимость использовать сторонний NHibernate Lambda Extensions чтобы избежать строковых констант типа criteria.CreateCriteria("Orders") )

    ОтветитьУдалить
  24. >>Про первые 3 версии EF я могу точно сказать, что они на не устраивают по многим причинам. Вот 4ая версия, уже подает надежды на зрелую ORM, которую можно было бы применить в проекте.

    Александр, не было первый трех версий: была первая (выпущенная впопыхах к релизу .NET 3.5 SP1), а потом появилась "четвертая" (синхронизировали с версией .NET) :)

    ОтветитьУдалить
  25. @Idsa
    Да, EF двигается довольно уверенно вперед. Если учесть, что многие разработчики просто "боятся использовать такого монстрака как NHibernate", то у EF возможно довольно хорошие перспективы.

    ОтветитьУдалить
  26. @Александр
    С выходом EF4, на мой взгляд, у EF одна перспектива - быть основной ORM под .NET. Тому есть ряд причин: во-первых, EF4 получился на славу (реализовали поддержку POCO, исправили работу со вторичными ключами, красиво интегрировали шаблоны T4 и т. д.), во-вторых, он сделан Microsoft и поставляется с .NET (а это серьезные аргументы для заказчиков).

    Будущее NHibernate - поддержка уже использующих его проектов.

    Несколько категорично, но мне так видится.

    ОтветитьУдалить
  27. @Александр
    Да, конечно. Правда, последний раз я его касался ровно год назад, но не думаю, что это принципиально (я надеюсь, поддержку LINQ уже допилили).

    Раз ты спрашиваешь о моем опыте с NHibernate, видимо, ты не согласен с моим мнением. Буду рад узнать, что не даст NHibernate потонуть.

    ОтветитьУдалить
  28. @Idsa
    Да, за год он очень сильно изменился. Скоро выходит 3я версия, там будет еще лучше.

    Я не знаю, что не даст ему потонуть :) Вообще он и не собирается тонуть. Скорее EntityFramework за Linq2Sql потонет, чем NHibernate.

    ОтветитьУдалить
  29. Хорошо, встречаемся через три года в этом же месте в то же самое время - и делаем выводы :)

    ОтветитьУдалить
  30. Валерий Игоревич20 августа 2013 г. в 00:50

    Я пришел на встречу. Делайте выводы! Я с удовольствием послушаю:)

    ОтветитьУдалить
  31. О, это интересно.

    В целом:
    - EF выпустили новую версию. Мы ее попробовали на новом проекте, но пришлось отказаться. Неоптимальные запросы, слишком долго идет маппинг выбранных данных в объекты. Эта ORM не потонула, живет и развивается.
    - NH развивается и испльзуется. Правда развитие не такое быстро как хотелось бы.
    - Там где важна скорость на первый план выходит Dapper http://blog.byndyu.ru/2013/03/dapper-queryobject-orm.html с очень быстрой скорость маппинга объектов.



    Вы что скажете?

    ОтветитьУдалить
  32. Здравствуйте, а как дела обстоят с fluentnhibernate.org ?

    ОтветитьУдалить
  33. В наших проектах мы используем NH только с FluentNH :)

    ОтветитьУдалить
  34. Валерий Игоревич23 августа 2013 г. в 22:04

    А по поводу LLBGEN v4 - есть у кого-нибудь опыт использования. На сайте все очень красиво написано, интересно как все это на самом деле работает.

    http://www.llblgen.com/pages/designer.aspx


    Multiple Object/Relational Mapper frameworks are supported
    -Entity Framework v1 (.NET 3.5 sp1), v4 (.NET 4), or v5 (.NET 4.5)
    -NHibernate (using hbm mappings & Fluent NHibernate mappings)
    -Linq to SQL
    -LLBLGen Pro runtime framework

    ОтветитьУдалить

Моя книга «Антихрупкость в IT»

Как достигать результатов в IT-проектах в условиях неопределённости. Подробнее...