Наши разработки. Владимир Репин.

dbВсе проекты нашей компании так или иначе связаны с базами данных. Во многих из них в качестве основного средства доступа используется Entity Framework. Преимуществ у этого средства много, среди них основые это скорость разработки и более красивый, переиспользуемый и поддерживаемый код. Это достигается за счет того, что код работающий с данными и запросы к базе пишутся на одном и том же языке, в отличие от ADO.Net, где приходилось бы «склеивать» строки для того, чтобы произвести запрос на языке SQL. Но есть у этого подхода и недостатки.  Цена удобной и быстрой разработки оказалась весьма высока. Производительность операций вставки, обновления и удаления данных оставляет желать лучшего. Причина такой производительности кроется в том, что все эти операции EF производит поодиночке. Т.е. если нам нужно вставить 100 записей в базу, то EF пошлет 100 запросов на вставку. Кроме этого, в одном из проектов была обнаружена одна неприятная ошибка. EF, при работе с Oracle, в Clob/Xml поля не позволяет вставлять строки более 2000 символов.
Для решения проблемы производительности и был создан компонент, который я назвал “Context Items”. Он создавался как часть комплексного решения, разрабатываемого для того, чтобы скомпенсировать недостатки Entity Framework-a, но для публикации я решил выделить Context Items как отдельный компонент, т.к. другая, более высокоуровневая часть еще не готова к тому, чтобы увидеть свет.

Компонент предоставляет следующие операции:

  1. Bulk Insert (MS Sql): в таблицы, не имеющие Identity в качестве первичного ключа возможно осуществить вставку методом Bulk Insert, который поддерживается базой данных Ms Sql Server. В случае с Identity нет способа надежно получить назад ключи, сгенерированные базой при вставке с помощью Bulk Insert, поэтому для таблиц имеющих такие ключи используется группировка нескольких обычных Insert-запросов в один запрос. Это работает существенно медленнее, чем Bulk Insert, но все же быстрее, чем через EF.
  2. Sequenced Bulk Insert (MS Sql): альтернативой Identity обычно служит Guid, это решает проблему вставки, но создает другую проблему – в силу большей длины ключа операции Join начинают работать медленнее, кроме этого Guid непоследователен, и поэтому Clustered индексы не приносят своих преимуществ. Как решение данной проблемы начиная с MS Sql Server 2012 есть возможность использовать Sequence для создания первичных ключей. Это позволяет использовать целочисленные последовательные ключи, что позволяет использовать Clustered индексы, аналогично Identity, и одновременно позволяет использовать Bulk Insert для вставки. Компонент поддерживает только ацикличные Sequence с инкрементом 1.
  3. Bulk Update (MS Sql): самой по себе в базе данных такой операции не существует, ее воплощает в себе компонент Context Items, последовательно выполняя следующие 4 операции:
    a)    Создается временная таблица, имеющая тот же набор полей, что и целевая таблица
    b)    Производится Bulk Insert данных во временную таблицу
    c)    Выполняется Join-Update операция, которая переносит данные из записей временной таблицы в записи целевой таблицы, имеющие совпадающие первичные ключи.
    d)    Временная таблица удаляется
    По причине того, что операция не атомарная, ее желательно исполнять в транзакции.
  4. Bulk Delete (MS Sql): также как и Bulk Update, эта операция происходит в 4 шага:
    a)    Cоздается временная таблица имеющая набор полей совпадающий с первичным ключом целевой таблицы
    b)    Производится Bulk Insert во временную таблицу первичных ключей для записей, которые надо удалить
    c)    Выполняется Join-Delete операция
    d)    Временная таблица удаляется
  5. Материализация: встроенная материализация в Entity Framework также работает не очень быстро, в силу того, что сущности нужно провести через механизм отслеживания изменений (Change Tracking), и еще каких то внутренних особенностей EF. Поэтому в Context Items так же добавлена возможность материализовать EF запрос непосредственно через ADO.Net. Кроме этого, эта операция исследовалась в сравнении с другими решениями, в частности достаточно известные micro-ORM Dapper. Context Items выполняет эту операцию примерно на 40% быстрее, чем EF и на 3-5% быстрее, чем Dapper.
  6. Array-bound Insert, Update and Delete (Oracle): Bulk Insert компонент, реализованный в ODP.Net не принимает во внимание ни триггеры ни constraint-ы, ни даже первичные ключи. Поэтому для вставки непосредственно в таблицу он не годится. Конечно всегда остается возможность использовать временную таблицу, но я решил использовать метод, которые рекомендуется самим разработчиком Oracle. Метод называется array-binding. Вкратце – запрос, посылаемый в базу, выглядит так, как будто мы вставляем одну запись, но в качестве параметров мы передаем не набор полей, а набор массивов полей, и таким образом вставляем, обновляем либо удаляем массив записей, а не одну запись.

Компонент Context Items поддерживает EF версии 5.0.0 – 6.1.3, а также базы данных Oracle и MS Sql Server.

Компонент опубликован на nuget.org, с идентификатором contextitems.
Подробнее об установке и использовании можно прочитать в репозитории на GitHub.com: https://github.com/repinvv/ContextItems

Владимир Репин, разработчик, Return on Intelligence, Нижний Новгород

Print Friendly, PDF & Email