Хотел сделать сравнение двух 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 есть очень удобный профайлер. Рекомендую!
Мне написал Frans Bouma, создатель LLBLGen. Франс перевел этот пост и написал мне письмо. Он заверил, что в 3-ей версии его ORM проблем, описанных мною, уже не будет. Могу только пожелать ему удачи! Ну, а как 3-ая версия выйдет, там и посмотрим ;)
Наш архитектор написал свои мысли по этому поводу.
Мда! С генераторами работать удобно и быстро, но когда заглядываешь под капот, то просто ужОсаешься. У меня так было с Linq2Sql. Пока не забросил идею пользовать генератор от МС, а писать руками маппинг. И проблема с бинарным содержимым тоже была, но в другой области - довелось работать с CrystalReport. А там этот "зверь" все генерит в бины, и ессно дифференс увидеть не получается.
ОтветитьУдалитьХотелось бы теперь увидеть новые статьи по NHibernate в твоем блоге.
Успехов!
Вопрос на засыпку, а почему нельзя было реализовать доступ к данным ручками? И какого типа ваше приложение? На мой взгляд NHibernete имеет низкую производительность
ОтветитьУдалить@DenDL
ОтветитьУдалить> Хотелось бы теперь увидеть новые статьи по NHibernate в твоем блоге.
Если что интересное под руку попадется, обязательно напишу.
@Nimnul
ОтветитьУдалить> почему нельзя было реализовать доступ к данным ручками?
Мы экономим свое время. Я считаю, что реализация очередной ORM (читай, очередного велосипеда с квадратными колесами) специально под свой проект - это пустая трата сил.
> И какого типа ваше приложение?
Проект состоит из разных приложений. В нем есть сайт, консольные приложения, WinForms, WCF-сервисы
> На мой взгляд NHibernete имеет низкую производительность
Здесь нужна ссылка на тесты производительности, иначе это утверждение не имеет смысла.
В любом случае, пока никаких проблем с производительностью замечено не было.
При выборе ORM инструмента для нового проекта отказались от LLBLGen в пользу Fluent NHibernate по тем же причинам, хотя оно и обеспечивает много готовой функциональности. Лучше дольше разрабатывать и потом продукт будет быстро бегать, чем наоборот.
ОтветитьУдалить@eye-ru
ОтветитьУдалить> Лучше дольше разрабатывать и потом продукт будет быстро бегать, чем наоборот
Согласен, основные расходы обычно идут на поддержку и расширение системы. Быстрый старт далеко не всегда является залогом успешности проекта.
а где же плюсы LLBLGen и недостатки NHibernate? ;)
ОтветитьУдалить@hazzik
ОтветитьУдалитьЯ в начале дал довольно много ссылок на статьи с их сравнениями. Целью статьи является показать причины отказа от LLBLGen.
Саня, напиши у себя какие плюсы ты видишь в LLBLGen и какие минусы у NHibernate!
"Быстрый старт далеко не всегда является залогом успешности проекта." - вот с этим полностью с тобой согласен. Точно подметил.
ОтветитьУдалитьАлександр, а рассматривали ли возможность применения Entity Framework ?
ОтветитьУдалитьЕсли да, то чем же он хуже NHibernate в вашем случае?
Просто интересно мнение...
@Tester
ОтветитьУдалитьСпасибо за вопрос!
Про первые 3 версии EF я могу точно сказать, что они на не устраивают по многим причинам. Вот 4ая версия, уже подает надежды на зрелую ORM, которую можно было бы применить в проекте.
Во-первых EF еще не выпущена. Во-вторых для нее нужен .NET 4 и новая Visual Studio. Это при том, что для нас она ничем не лучше NHibernate.
Т.е. в перспективе возможно мы будем использовать EF, но для этого должны быть довольно весомые аргументы.
Если вы применяете EF, то было бы очень интересно увидеть сравнение этой ORM с NHibernate.
Ну если у вас есть много времени, что бы разбираться с глюками ORM то пожалуйста. Нащет производительности, сделай простой тест получения случайных(по коду) данных в десяти потоках и все увидите.
ОтветитьУдалить@Nimnul
ОтветитьУдалить> Ну если у вас есть много времени, что бы разбираться с глюками ORM то пожалуйста
Это ваша интерпретация каких-то конкретных проблем. Опишите их пожалуйста.
> сделай простой тест получения случайных(по коду) данных в десяти потоках и все увидите.
Если честно, то не понятно, зачем в 10ти потоках получать случайные данные. В любом случае вы можете прислать код, который медленно/нестабильно/неправильно работает в NHibernate. Я думаю будет интересно выяснить причины такой работы, возможно написать об этом разработчикам.
Александр, спасибо за сравнение.
ОтветитьУдалитьЧто касается производительности 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
Коммент очень большой, разбил на два. Сорри :(
Продолжение:
ОтветитьУдалитьНо не нужно забывать, что:
а) синтетические тесты имеют мало общего с реальностью, поэтому не стоит им сильно доверять,
б) каждый 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
@Александр Кондуфоров
ОтветитьУдалить> из предыдущего пункта следует, что некоторые задачи и даже типы приложений, требующие быстрой скорости выполнения, не следует решать/создавать при помощи ORM, полностью или частично, нужно всегда думать головой.
Да-да-да! Если вы занимаетесь бухгалтерией, то не надо писать свою CMS, если вы занимаетесь разработкой сайтов, то не надо писать свою 1С.
@Nimnul, как раз такие ситуации я и называл изобретением велосипеда с квадратными колесами.
Александр, спасибо за ссылки!
Спасибо за ответ.... :)
ОтветитьУдалить> Если вы применяете EF, то было бы очень интересно увидеть сравнение этой ORM с NHibernate.
Нет, я EF не применял... потому и интересно... когда мне нужно было сделать выбор в ORM - EF была еще в бете. И, среди бесплатных выбрал SOODA - её автор сейчас работает в MS над EF и, думаю, Lazy Loading в EF - это его заслуга.
SOODA меня устраивала, да и пока устраивает в текущих проектах, но есть некоторые моменты, из-за которых я поглядываю на другие ORM, но пока явного кандидата на замену не вижу....
@Tester
ОтветитьУдалитьВозможно вам понравится NHibernate. На данный момент это уже зрелый продукт, который можно без опасений использовать в своих приложениях.
Что меня убивает так это то что нужно прописывать sqlServerCatalogNameOverwrites чтобы база не слетала... а это значить что хостить несколько баз в одном конфиге проблематично... если только не использовать для других баз другие ORM.
ОтветитьУдалитьИспользую LLBLGEN уже 7 лет и указанные "недостатки" не мешали работать.
ОтветитьУдалитьПро тормознутость первый раз слышу. Хотя согласен, если архитектура базируется на чистых доменных объектах то LLBLGEN для нее не подходит. Но не доменом единым жив человек :)
Может я видел кривое применение NHibernate но извините меня, когда нужно было реализовать сложный фильтр на основе критериев введенных через GUI то в nHibernate коде я увидел запросы к базе практически hardcoded. Это не то что плохо, это ужасно.
Я спросил разработчика - почему так, он сказал что NHibernate бессилен в таких случаях.
LLBGEN в этом отношении имеет свой объектный Predicate механизм который позволяет реализовывать динамическую фильтрацию данных при выборке из базы.
Да и LINQ начиная с версии 2.6 поддерживает.
sqlServerCatalogNameOverwrites существует только в SelfServicing. В случае с Adapter пишете свой CustomDataAdapter наследовавшись от DataAccessAdapter где считывайте Connection String откуда и как захотите.
ОтветитьУдалить@Dmitri Demyanik
ОтветитьУдалитьЯ только рад, если LLBLGen подходит для ваших проектов.
NHibernate тоже не стоит на месте. Попробуйте новые версии. Если вы последний раз использовали NH год назад, то доработки вас очень порадуют.
Александр,
ОтветитьУдалитьвсе гораздо хуже, пользовался NH 5 лет назад! :)
Хотя глядя на эту статью вижу что действительно есть движение вперед
http://slynetblog.blogspot.com/2009/11/nhibernate-4-criteria-queries.html
(Смущает правда необходимость использовать сторонний NHibernate Lambda Extensions чтобы избежать строковых констант типа criteria.CreateCriteria("Orders") )
>>Про первые 3 версии EF я могу точно сказать, что они на не устраивают по многим причинам. Вот 4ая версия, уже подает надежды на зрелую ORM, которую можно было бы применить в проекте.
ОтветитьУдалитьАлександр, не было первый трех версий: была первая (выпущенная впопыхах к релизу .NET 3.5 SP1), а потом появилась "четвертая" (синхронизировали с версией .NET) :)
@Idsa
ОтветитьУдалитьИ?
И вот :)
ОтветитьУдалить@Idsa
ОтветитьУдалитьДа, EF двигается довольно уверенно вперед. Если учесть, что многие разработчики просто "боятся использовать такого монстрака как NHibernate", то у EF возможно довольно хорошие перспективы.
@Александр
ОтветитьУдалитьС выходом EF4, на мой взгляд, у EF одна перспектива - быть основной ORM под .NET. Тому есть ряд причин: во-первых, EF4 получился на славу (реализовали поддержку POCO, исправили работу со вторичными ключами, красиво интегрировали шаблоны T4 и т. д.), во-вторых, он сделан Microsoft и поставляется с .NET (а это серьезные аргументы для заказчиков).
Будущее NHibernate - поддержка уже использующих его проектов.
Несколько категорично, но мне так видится.
@Idsa
ОтветитьУдалитьА ты использовал NHibernate?
@Александр
ОтветитьУдалитьДа, конечно. Правда, последний раз я его касался ровно год назад, но не думаю, что это принципиально (я надеюсь, поддержку LINQ уже допилили).
Раз ты спрашиваешь о моем опыте с NHibernate, видимо, ты не согласен с моим мнением. Буду рад узнать, что не даст NHibernate потонуть.
@Idsa
ОтветитьУдалитьДа, за год он очень сильно изменился. Скоро выходит 3я версия, там будет еще лучше.
Я не знаю, что не даст ему потонуть :) Вообще он и не собирается тонуть. Скорее EntityFramework за Linq2Sql потонет, чем NHibernate.
Хорошо, встречаемся через три года в этом же месте в то же самое время - и делаем выводы :)
ОтветитьУдалитьЯ пришел на встречу. Делайте выводы! Я с удовольствием послушаю:)
ОтветитьУдалитьО, это интересно.
ОтветитьУдалитьВ целом:
- EF выпустили новую версию. Мы ее попробовали на новом проекте, но пришлось отказаться. Неоптимальные запросы, слишком долго идет маппинг выбранных данных в объекты. Эта ORM не потонула, живет и развивается.
- NH развивается и испльзуется. Правда развитие не такое быстро как хотелось бы.
- Там где важна скорость на первый план выходит Dapper http://blog.byndyu.ru/2013/03/dapper-queryobject-orm.html с очень быстрой скорость маппинга объектов.
Вы что скажете?
Здравствуйте, а как дела обстоят с fluentnhibernate.org ?
ОтветитьУдалитьВ наших проектах мы используем NH только с FluentNH :)
ОтветитьУдалитьА по поводу 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