Dapper – micro-ORM — I

Недавно я рассказывал про легковесную ORM BLToolkit, и при поиске и изучении материала неизбежно наталкивался на сравнение BLT с другими разработками в области мапирования данных на бизнес-объекты. Одним из самых привлекательных вариантов по скорости, а так же по вниманию общественности, оказался Dapper.

Dapper – это даже не легковесная, а микро-ORM система для чтения (в основном) информации из реляционных баз данных. Данная микро-ORM система является разработкой Сэма Сафрона (Sam Saffron) для Stack Overflow, где она работает в связке с Linq2Sql. Для такого большого и посещаемого ресурса как Stack Overflow очень важно быстро получать информацию из базы данных, так как большинство пользователей просматривает ответы, использует их в своей работе, и сравнительно редко пишет. Для записи информации, что требуется значительно реже, до сих пор самым удобным и быстрым остается Linq2Sql.

О системе

Dapper – это по сути один файл с исходным кодом, который надо включить в свой проект. Dapper работает в некотором роде классом помощником, расширяя стандартный интерфейс IDbConnection с помощью extended методов. Т.е. данному фреймворку абсолютно без разницы, с какой базой вы работаете, если соединение с ней построено на указанном интерфейсе.

Запросы Dapper принимает только в текстовом виде, нет поддержки LINQ. Модификация бизнес-классов в основном не требуется, в редких случая потребуется использовать свойства вместо полей класса.

В Dapper нет поддержки маппинга сложных связанных объектов, так же как и в BLToolkit.

Ключевой особенностью Dapper является скорость и на страничке проекта приведены данные измерений времени, которое необходимо для выполнения 500 запросов выборки.

Производительность для операции SELECT для 500 итераций с мапингом — POCO объекты

Реализация Длительность Заметки
Hand coded (using a SqlDataReader) 47ms
Dapper ExecuteMapperQuery<Post> 49ms
PetaPoco 52ms Can be faster
BLToolkit 80ms
SubSonic CodingHorror 107ms
NHibernate SQL 104ms
Linq 2 SQL ExecuteQuery 181ms
Entity framework ExecuteStoreQuery 631ms

 

Производительность для операции SELECT для 500 итераций с мапингом — динамические объекты

Реализация Длительность Заметки
Dapper ExecuteMapperQuery (dynamic) 48ms
Massive 52ms
Simple.Data 95ms

 

Производительность для операции SELECT для 500 итераций с мапингом – типичное использование (сложный маппинг  объектов)

Реализация Длительность Заметки
Linq 2 SQL CompiledQuery 81ms Не совсем типичное использование, привлекается много сложносоставных объектов
NHibernate HQL 118ms
Linq 2 SQL 559ms
Entity framework 859ms
SubSonic ActiveRecord.SingleOrDefault 3619ms

 

Все тесты можно взять со страницы проекта в виде отдельного файла с C# классом.

Ограничения и оговорки

Каждый запрос к базе данных кэшируется, что позволяет быстро материализовывать объекты и распознавать параметры запросов. Текущая реализация кэша основана на ConcurrentDictionary. Объекты попадающие в это хранилище не очищаются во время работы приложения, так что если вы генерируете SQL запросы на ходу без использования параметров, возможны проблемы с памятью, так количество уникальных запросов сильно вырастет.

Простота фреймворка означает, что многие возможности которые идут с полновесными ORM опущены. Например нет identity map, нет помощников для обновления/выборки данных и так далее.

Так же Dapper не управляет временем жизни соединения и он предполагает что соединение уже открыто И используется монопольно, т.е. не существует других процессов читающих данные в текущем соединении. Если только не задействована опция MARS — Multiple Active Result Sets.

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

Установка

Установить dapper легче всего с помощью NuGet. На данный момент на сайте указано, что рекомендуемая версия 1.7 и выпущена она  5 ноября 2011 года.

 

Результат:

 

И новая папка с файлом в проекте. Что достаточно необычно для распространения через NuGet. Зато все желающие могут почитать код и осознать, насколько много вещей еще можно изучать.

Или как уже было сказано выше, можете просто включить файл с реализацией Dapper в проект.

Подготовка

Проверять удобство работы будем на тех же классах и данных, что были в статье про BLToolkit. Однако все равно наверно лучше указать здесь все необходимые скрипты и классы.

В качестве эксперимента у нас будут выступать некоторые сводные данные по людям, и их адреса. Итак, скрипт для создания данных в базе:

 

Чтобы два раза не вставать создали сразу все таблицы и заполнили их данными.

Сразу создадим классы, которые будем заполнять информацией из базы данных:

 

Все, структуры данных готовы, можно приступать к экспериментам. Ах да, еще надо упомянуть про отдельное создание соединения с базой данных, так как Dapper сам не контролирует время жизни соединения. Создадим отдельный класс, в котором будем писать методы с различными вариантами работы фреймворка. Пусть этот класс называется Dapper, и конструктор будет создавать соединение с базой данных.

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

 

Базовые возможности

У нас все готово к тому, чтобы начать работу с Dapper:

  • Таблицы и данные готовы
  • Классы в C# коде есть
  • Dapper в проект включен

Простой запрос

Начнем с самого простого, что можно только себе представить. Сделаем выборку полной таблицы Person и заполним соответствующий класс.  Для этого воспользуемся методом расширения со строгой типизацией Query, который принимает текст запроса в качестве обязательного параметра.

Открываем соединение с базой,  пишем текст запроса, вызываем метод расширения для соединения, получаем готовые классы. Профит! Легко и быстро до безобразия. Однако метод Query не так прост, у него много необязательных параметров для тонкой настройки. Полная сигнатура выглядит так:

 

  • Sql – текст запроса,
  • Param – значения параметров, если они включены в запрос
  • Transaction – включение в транзакцию
  • Buffered – полное чтение результатов или по мере необходимости
  • CommandTimeout – ограничение времени выполнения запроса
  • CommandType – тип запроса: запрос, команда

Запрос с параметрами

Далее, немаловажно уметь передавать параметры в запрос. В запросе параметры следуют нотации MSSQL, т.е. начинаются со знака @. Параметры передаются в виде анонимного класса, и только в виде него. Имена полей должны соответствовать именам параметрам, причем регистр имеет значение. Это правило действует для всех параметров.

 

Оказалось не сложно, верно?

Простой не типизированный запрос

Можно опустить строгую типизацию и получить результат в виде dynamic типа.

Команда, возвращающая набор данных

Работа с хранимыми процедурами не сильно отличается от работы с простыми запросами. Необходимо указать имя процедуры и что это все же процедура, а не запрос.

В данном случае использован строго типизированный запрос, и чтобы не писать множество null, я использовал именованный параметр для указания типа запроса. CommandType взят из пространства имен System.Data.

Как видите и здесь нет ничего сложного, подводных камней нет тоже никаких. Вообще пользоваться Dapper’ом оказалось весьма просто и легко.

Команды без результирующего набора данных

Естественно, что не все команды возвращают набор данных в результате своей работы. Для таких вызовов предназначен другой метод расширяющий интерфейс IDbConnetction – Execute.

Метод Execute в качестве обязательного параметра принимает текст запроса, опциональным является тип запроса.  Вообще данная команда предназначена для любого типа операций, которые не возвращают данных.

Результатом операции будет целое число, говорящее о количестве измененных строк.

Полная сигнатура метода:

 

На этом можно завершить рассказ о базовых возможностях фреймворка.

 

Продолжение следует

Оставить комментарий