Интересности      Книги      Утилиты    

16 января 2018 г.

Когда не нужен ORM а нужно что-то поменьше. Dapper micro-ORM

Untitled
Пишем слой доступа к данным? И сразу слышим ответ – Entity Framework или может от бывалого - nHibernate . Программистам подсевшим на ORM в типовых проектах сложно поверить, что есть проекты, которые пишут еще на ADO.NET. Почему? Потому что он быстрый, любая дополнительная прослойка это замедление. Плюс дополнительная библиотека, а если в команде с ней работать умеете только вы, нужно обучать коллег. А если не работаете и вы в том числе, то вы в конечном счете научитесь попутно понаступав на грабли, что обещает вам много ваших вечеров наедине с работой. 
Многие проекты и в том числе новые работают с ADO.NET и по разным причинам. Производительность, полный контроль над запросами и кодом доступа к базе, серьезный и сложный Database back-end c полным фаршем сервисов (Stored procedures, OLAP кубы, Analysis Services и т.д. и т.п.), наличие квалифицированных Database Engineer. Кто-то использует вещи очень специфические для СУБД что нет смысла потом извращать ORM и выполнять через ORM запросы в чистом виде. А для кого-то по большому счету из возможностей, которые дают ORM нужно всего на всего замапить результат запроса из базы на свои объекты. Им не нужна сложность, которые привносят ORM, тратить время на проверку насколько быстро что работает, какие запросы генерятся, как оптимизировать и т.п.
Очень просто маппер результатов запросов c базы на объекты можно написать и самому. Но как обычно бывает с такими библиотеками, вы тратите на него время, а получаете не всегда универсальную библиотеку, которую можно потом переиспользовать на другом проекте. И всегда перед тем как выдумывать велосипед сначала стоит поискать и оценить что-то уже готовое.
Если постараться то можно найти достаточно много проектов “micro ORM” библиотек. Среди них Dapper выделяется тем, что его написали и используют для себя на stackoverflow.com. Тем не менее они могут заслуживать вашего внимания – Massive, Simple.Data, PetaPoco, ServiceStack.OrmLite, Insight.Database, SqlFu, и т.д. Каждый из них имеет свои позитивчики, например Simple.Data позволяет вызывать хранимые процедуры как методы используя dynamic или ServiceStack.OrmLite часть фреймворка ServiceStack и почти такой же быстрый как Dapper. Ну и главный позитив у них, что их код открыт и вы можете почерпунить из реализации для себя много новых идей.
Если сравнивать скорость работы. То на страницах Dapper и ServiceStack есть довольно старый бенчмарк, более свежие найти не так уж сложно, или даже сделать свой. Суть даже не в свежести, разработка продолжается, производительность повышают, добавляют новые возможности, но судить о разнице в производительности можно.
ScreenClip
Micro ORM почти так же быстродейственны  как и вручную написанный слой доступа к данным, и приблизительно в 2 раза быстрее большинства ORM. C другой стороны тот же nHibernate имеет значительно больше фич и возможностей, хорошую документацию и комьюнити и проверку временем. Стоит ли выбирать micro ORM как например альтернативу ему? Решать вам. C другой стороны если у вас самописаный слой доступа к данным с помощью ADO.NET то можно внедрить micro ORM  чтобы уменьшить рутинные операции, при этом код особо не усложняется.
Хоть эти библиотеки достаточно маленькие, но часто не особо блещут документацией. Если использовать Dapper, то его стоит использовать с Dapper Extensions, так как без Dapper Extensions он не умеет делать Insert, Update, Delete операции, плохо работает с композитными ключами, GUID Primary keys. В моем случае когда данные получаются через хранимые процедуры и  модифицируются также через них это не имеет значения. GUID Primary keys или композитные ключи – не типичные сценарии. Но тем не менее вместе с Dapper Extensions эти проблемы отпадают сами собой. Оба проекта находятся с открытым кодом на гитхабе:
Dapper
https://github.com/SamSaffron/dapper-dot-net
Dapper Extensions
https://github.com/tmsmith/Dapper-Extensions
Наверное пожалуй самое основное что от Dapper нужно это автоматически замаппить результат из базы на объекты.  Например ниже код который выполняет хранимую процедуру и получает данные с помощью DataAdapter. При этом генерится куча мусора и в последствии эти типизированные DataSet достаточно тяжело менять, если в базы были какие-то изменения.

Можно также выполнить запрос и замапить объекты самостоятельно без адаптеров:

Ничего сложно но если код доступа к базе меняется то необходимо постоянно писать рутинный код маппинга в базу.
Ну и код с помощью Dapper:

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

Dapper также поддерживает маппинг объектов, когда имена свойств в POCO и имена колонок в базе отличаются (custom mapping). Например в коде объекта ReportInfo использовался один из стандартных для .NET атрибутов для этой цели Description. Код мапинга с ним приведен ниже и его легко поменять например на свой атрибут:

Еще больше примеров можно найти на странице проекта на github.
Для тех кто считает что ORM - anti pattern, mini ORM и в частности Dapper тоже подойдут.
В качестве итога можно сказать, что из позитивных сторон у Dapper стоит назвать – легкость установки (одна-две небольшие сборки если с Dapper Extensions, или второй вариант только Dapper cs-файл размером в 3,3К строчек кода), быстродействие, легкость в понимании, ну и то что его успешно используют на stackoverflow (references никто не отменял, часто тоже важный фактор в принятии решения). Стоит ли его использовать как альтернативу полноценной ORM? Все зависит от вас и от быстродействия каждого конкретного ORM, и как ORM используется. Стоит ли его использовать если вы пишете свой уровень доступа к данным используя ADO.NET? В моем случае да, так как это позволило сократить похожий и рутинный код и операции, и при этом ничего не потерять в сложности и быстродействии.
Что почитать дальше? Кроме ссылок в самом посте, еще можно почитать серию блог постов со сравнением Dapper, Massive и PetaPoco.

2 комментария:

  1. Юзаю Dapper на поточному прожекті. Є ньюанси з stored procedures але в цілому задоволений.

    ОтветитьУдалить
  2. А які ньюанси? Тому що я також через stored procedures використовую і все нормально

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