- Регистрация
- 09.10.2015
- Сообщения
- 3 916
- Реакции
- 3 883
- Баллы
- 113
Небольшая библиотека для упрощения работы с SQLite базой данных в ZennoPoster.
В первую очередь, данная библиотека поможет исключить множество повторяющегося кода (перехват ошибок, создание SQLiteCommand на каждый запрос, правильную передачу параметров через SQLiteCommand.Parameters). А также, облегчит разработку и упростит обновление и изменение ваших проектов по ходу их развития.
Идея и часть реализации подсмотрена у @DmitryAk + PHP PDO database framework Medoo.
Текущий релиз не избавляет полностью от работы с System.Data.SQLite объектами + содержит несколько изъянов, которые могут допустить SQL-injection (или скорее порчу данных), но даже эта версия уже пригодна для использования (аккуратного).
Установка
Советы по использованию
Описание методов FastSqliteHelper
С полным описанием методов (их параметрами и возвращаемыми значениями) вы можете ознакомиться на странице в github-репозитории.
TODO
Ниже описан план развития, задачи из которого по моему мнению нужно сделать в первую очередь, минуя добавление другого функционала, который может быть реализован непосредственно через работу с System.Data.SQLite.
Если у вас есть непреодолимое желание помочь этому проекту в развитии - посмотрите ниже, что я хотел бы видеть в следующих версиях этой библиотеки, сделайте Fork репозитория и отправьте свой Pull Request с решением любой из задач
Послесловие
В прикрепленных ниже файлах вы найдете архив со скомпилированными библиотеками и тестовый шаблон с примерами работы практически всех методов. Вся дополнительная документация и весь исходный код опубликованы в github-репозитории: https://github.com/lord-alfred/FastSqliteHelper
На самом деле, изначально я отправлял заявку на конкурс для совсем другого шаблона, но в ходе его написания появилась острая необходимость в таком легковесном SQLite C# wrapper, чтобы было удобнее манипулировать данными. Но это уже совсем другая история...)
В первую очередь, данная библиотека поможет исключить множество повторяющегося кода (перехват ошибок, создание SQLiteCommand на каждый запрос, правильную передачу параметров через SQLiteCommand.Parameters). А также, облегчит разработку и упростит обновление и изменение ваших проектов по ходу их развития.
Идея и часть реализации подсмотрена у @DmitryAk + PHP PDO database framework Medoo.
Текущий релиз не избавляет полностью от работы с System.Data.SQLite объектами + содержит несколько изъянов, которые могут допустить SQL-injection (или скорее порчу данных), но даже эта версия уже пригодна для использования (аккуратного).
Установка
- Скомпилировать самостоятельно или взять последнюю версию FastSqliteHelper.dll из релизов и положить в директорию:
Код:C:\Program Files (x86)\ZennoLab\RU\ZennoPoster Pro\[версия ZP]\Progs\ExternalAssemblies - Скопировать System.Data.SQLite.dll и SQLite.Interop.dll из папки sqlite-netFx46-binary-x64-2015-1.0.107.0 (или скачать из последнего релиза) в вышеприведенную директорию ExternalAssemblies
- В проекте выбрать Добавить действие -> Свой код -> Ссылки из GAC
- Зайти в появившийся внизу блок References (в Расширенном редакторе), нажать кнопку Добавить..., затем кнопку Обзор...
- В появившемся окне выбрать:
Код:C:\Program Files (x86)\ZennoLab\RU\ZennoPoster Pro\[версия ZP]\Progs\ExternalAssemblies\FastSqliteHelper.dll - Повторить пункт 4 и в появившемся окне выбрать:
Код:C:\Program Files (x86)\ZennoLab\RU\ZennoPoster Pro\[версия ZP]\Progs\ExternalAssemblies\System.Data.SQLite.dll - В проекте выбрать Добавить действие -> Свой код -> Директивы using и общий код
- Зайти в появившийся внизу блок OwnCodeUsings (в Расширенном редакторе) и в окне "Директивы Using" вставить:
C#:using FastSqliteHelperLib; using System.Data.SQLite;
Советы по использованию
- Примеры работы практически со всеми методами есть в шаблоне test_project.xmlz
- При использовании многопоточных шаблонов в ZennoPoster всегда добавляйте к строке дополнительных параметров connection string в методе FastSqliteHelper.Init включение типа журнала WAL с помощью:
С ним скорее всего станет возможно писать и читать из одной таблицы без получения состояния "database is locked" в многопотоке. Но появятся дополнительные файлы (*-shm и *-wal), при этом вам нужно будет решить: оставить фиксацию измененных данных в автоматическом режиме или вручную делать так называемый "checkpoint" (везде советуют делать его в отдельном процессе, когда работа с базой в этот момент сведена к минимуму).Код:Version=3;Journal Mode=WAL;
- Всегда закрывайте соединение с базой данных через метод FastSqliteHelper.DeInit.
- Если в результате выполнения какого-либо из методов в результате возвращается объект класса System.Data.SQLite.SQLiteDataReader ("читатель"), то необходимо всегда закрывать его через метод .Close, чтобы избежать ошибок в многопотоке.
Пример корректного закрытия "читателя" при получения множества строк с помощью метода FastSqliteHelper.Select:
Пример корректного закрытия "читателя" при получении единственного поля из единственной строки с помощью метода FastSqliteHelper.Select:C#:System.Data.SQLite.SQLiteDataReader reader = FastSqliteHelper.Select("table_name", new string[]{"id", "value"}); try { foreach(dynamic row in reader) { project.SendInfoToLog("Строка: " + row["id"] + " -> " + row["value"]); } } finally { reader.Close(); }
C#:System.Data.SQLite.SQLiteDataReader reader = FastSqliteHelper.Select("table_name", "count(id) as count_all"); int count = 0; try { if (reader.Read()) { count = Convert.ToInt32(reader["count_all"]); } } finally { reader.Close(); }
- Если в каком-то условии (condition) для выборки/обновления/удаления вам нужно сделать LIMIT, ORDER BY или что-то, что идет за WHERE - не забывайте, что в библиотеке это условие конкатенуется ("склеивается") с:
Поэтому, если вам не нужно указывать критерий для выборки (WHERE) - лучше добавить в условие что-то вроде (пример для LIMIT):Код:WHERE 1=1 and
Для того, чтобы избежать ошибки syntax error.Код:2=2 LIMIT 0,10
- Для массового добавления строк в таблицу необходимо открывать транзакцию (пример реализации работы через транзакцию есть в методе FastSqliteHelper.Insert, но он может быть неудобен для ваших задач, т.к. предварительно нужно будет сделать список словарей с данными для вставки). Если не открывать транзакцию, то SQLite сделает это неявно на каждый Insert, поэтому у вас с очень большой долей вероятности будут "тормоза". Также, для ускорения процесса вставки (только при использовании в однопоточном шаблоне!) лучше отключить тип журнала и тип синхронизации при подключении к БД:
Код:Journal Mode=OFF;Synchronous=0;
- В SQLite при дефолтном "journal_mode" очень просто поймать состояние "database is locked", оно возникает в том случае, если первый поток читает данные из таблицы, а второй поток туда пишет. Чтобы предотвратить такого рода поведение - нужно заранее продумывать логику работы шаблона и обязательно использовать оператор блокировки:
Плюс, как было написано в самом начале - лучше использовать тип журнала WAL, но не факт, что это даст 100% гарантии того, что шаблон не попадет в такое состояние.C#:lock(YourOwn.LockerObject) { // работа с БД через методы FastSqliteHelper }
- Всегда явно указывайте столбцы для выборки, избегайте "*" для возвращения всех столбцов из таблицы. Это ускорит работу и поможет избежать ошибок, допущенных по невнимательности.
- В методе подключения к БД есть один очень важный параметр: throw_exc_on_errors - "выдавать ли исключение в случае ошибки или просто по-тихому писать в лог ZP". Я настоятельно рекомендую выставлять его всегда в true, чтобы в случае ошибки - шаблон останавливал свое выполнение и прекращал работу, минуя тем самым другие ошибки, которые могут произойти далее.
- К сожалению, в текущей реализации невозможно работать с 2 и более более SQLite базами данных одновременно. Это ограничение связано с тем, что класс FastSqliteHelper и его методы - статические. Можно сделать "хак" с monkey-patching объекта подключения и переопределять его "на лету", но это не тот подход, который хотелось бы предлагать для использования. Возможно, когда-то в дальнейшем эта оплошность будет устранена.
- Для удобного просмотра/изменения базы данных я советую использовать бесплатную программу SQLiteStudio (в ней есть русификация).
- Помните, что SQLite в первую очередь - это встраиваемая база данных в одном файле, поэтому ожидать от неё существенного прироста производительности - не стоит. Для таких целей лучше использовать MySQL или PostgreSQL. Но в целом, для небольших проектов/шаблонов, в которых не будет миллионов строк в базе - это очень хорошее решение, которое поможет избавиться от "списков" и "таблиц" в ZennoPoster.
Советую к прочтению небольшой цикл статей о SQLite на хабре.
Описание методов FastSqliteHelper
- FastSqliteHelper.Init — Инициализация подключения к SQLite базе данных.
- FastSqliteHelper.Init — Инициализация подключения к SQLite базе данных (дополнительные параметры connection string в виде словаря).
- FastSqliteHelper.DeInit — Закрытие подключения к базе данных.
- FastSqliteHelper.PragmaSet — Установка параметра PRAGMA для текущего подключения к БД.
- FastSqliteHelper.PragmaGet — Чтение параметра PRAGMA из текущего подключения к БД.
- FastSqliteHelper.Select — Получение данных из таблицы по условию (столбцы в виде строки).
- FastSqliteHelper.Select — Получение данных из таблицы по условию (столбцы в виде массива строк).
- FastSqliteHelper.Select — Получение данных из таблицы по условию (столбцы в виде списка).
- FastSqliteHelper.Insert — Добавление одной записи в таблицу.
- FastSqliteHelper.Insert — Добавление множества записей в таблицу (используя транзакцию на все добавляемые данные).
- FastSqliteHelper.LastInsertID — Идентификатор последней добавленной записи.
- FastSqliteHelper.Update — Обновление значений в таблице по указанному условию.
- FastSqliteHelper.Update — Обновление значения одного столбца в таблице по указанному условию.
- FastSqliteHelper.Delete — Удаление записей из таблицы по указанному условию в виде строки.
- FastSqliteHelper.Delete — Удаление записей из таблицы по указанному условию в виде словаря.
- FastSqliteHelper.Query — Любой запрос к БД, возвращающий количество затронутых строк.
- FastSqliteHelper.QueryReader — Любой запрос к БД, возвращающий объект SQLiteDataReader с содержимым.
- FastSqliteHelper.QueryScalar — Любой запрос к БД, возвращающий первый столбец первой строки.
- FastSqliteHelper.GetLastQuery — Получить последний отправленный запрос (включая запросы, в ходе обработки которых возникла ошибка).
TODO
Ниже описан план развития, задачи из которого по моему мнению нужно сделать в первую очередь, минуя добавление другого функционала, который может быть реализован непосредственно через работу с System.Data.SQLite.
Если у вас есть непреодолимое желание помочь этому проекту в развитии - посмотрите ниже, что я хотел бы видеть в следующих версиях этой библиотеки, сделайте Fork репозитория и отправьте свой Pull Request с решением любой из задач

- Написать тесты для каждого метода, чтобы в случае выпуска нового релиза можно было проверить не сломалось ли что-то где-то.
- Реализовать тестовый проект для консольного приложения, чтобы не было нужды проверять все методы в ProjectMaker, а была возможность работы в VisualStudio/SharpDevelop.
- Проверить и разобраться почему при установке какого-либо значения PRAGMA через строку подключения - чтение этого параметра через метод PragmaGet получает другой результат.
- Избавиться от возвращения SQLiteDataReader в нескольких методах, чтобы можно было не думать о том, что его нужно всегда "закрывать".
- Облегчить работу с транзакциями (избавиться от явного вызова Commit/Rollback), используя анонимные функции (делегаты в C#?).
Пример правильной организации выполнения запросов внутри транзакции на PHP: https://medoo.in/api/action - Реализовать передачу условий в методы, чтобы они не "склеивались" с условием WHERE 1=1 and, чтобы была возможность передать LIMIT 0,10 без добавления конструкции вида 2=2.
- Продумать и реализовать способ передачи условий в виде "столбец" => "значение" без ограничения на то, что все имена столбцов должны быть уникальны.
- Добавить метод для экранирования значений, которые могут быть в дальнейшем переданы в виде plain-запроса в методы Query*.
Пример похожего функционала в PHP PDO драйвере: http://php.net/manual/ru/pdo.quote.php - Добавить возможность работы с несколькими SQLite базами одновременно (убрать статический модификатор для класса, но это повлечет проблемы при передаче объекта через project.Context в ProjectMaker или сделать поле connection в виде словаря подключений, но нужно будет предварительно протестировать этот вариант в многопоточном режиме, чтоб не вышло, что данные будут пересекаться).
- Переименовать методы Init -> Open, DeInit -> Close. Сомнительно и добавит обратную несовместимость, но читаемость кода повысится.
- Реализовать получение/вставку/обновление/удаление данных через объекты, а не через словари. Плюс - не нужно будет делать некрасивые словари для работы с этими методами; минус - для каждой таблицы, с которой нужно будет работать - нужно будет создавать классы и вести работу через них.
- Проверить и исправить все методы, которые подвержены sql-injection, чтобы в ходе работы не испортились данные.
- В методах, которые не требуют работы с параметрами (SQLiteCommand.Parameters) - переписать отправку запроса/получение результата через внутренние методы Query*. Но это может привести к тому, что будет сложно разобрать в сообщении об ошибке - откуда именно упало исключение (поэтому данная задача под сомнением).
- Добавить метод, который будет возвращать последний выполненный запрос (или даже лучше - отправленный, чтобы в случае перехвата ошибки внутри метода его можно было отобразить).
Пример такого функционала в PHP фреймворке Medoo: https://medoo.in/api/last - Подумать над тем, что в последний отправленный запрос может быть списком (логом) всех запросов. Плюс решить стоит ли там заменять параметры на их значения.
- Написать Contributing Guidelines, которые будут включать в себя: правильную настройку редактора, чтобы избежать табуляций в коде; использование String.Format и String.Concat; удаление trailing whitespace и другие полезные вещи.
Послесловие
В прикрепленных ниже файлах вы найдете архив со скомпилированными библиотеками и тестовый шаблон с примерами работы практически всех методов. Вся дополнительная документация и весь исходный код опубликованы в github-репозитории: https://github.com/lord-alfred/FastSqliteHelper
На самом деле, изначально я отправлял заявку на конкурс для совсем другого шаблона, но в ходе его написания появилась острая необходимость в таком легковесном SQLite C# wrapper, чтобы было удобнее манипулировать данными. Но это уже совсем другая история...)
- Номер конкурса шаблонов
- Первый конкурс шаблонов
- Уровень сложности
- Продвинутый
- Категория
- Полезно
Вложения
Последнее редактирование:



