RedBase 3.2.0 — SQLite-провайдер: один файл, офлайн, Blazor WASM и мобилки
С тех пор как мы выложили RedBase в опенсорс, чаще всего спрашивали одно и то же: «а под SQLite будет?» — и почти всегда по двум причинам. Первая — Blazor WebAssembly: типизированное объектное хранилище прямо в браузере, без похода на бэкенд. Вторая — мобилки и десктоп (MAUI, offline-first, один файл .db, который едет вместе с приложением). Postgres и MSSql — прекрасные серверы, но их не встроишь в iOS-приложение и не положишь в WASM-бандл.
В RedBase 3.2.0 вышел SQLite-провайдер. Тот же LINQ, та же модель из 13 таблиц, та же регистрация AddRedb(...), что и для Postgres/MSSql — только теперь Data Source=app.db.
Если про REDB впервые — короткий контекст из предыдущих статей серии:
- redb — типизированное хранилище для .NET поверх Postgres/MSSQL: без миграций, без Include, с полным LINQ — что это вообще и почему без миграций
- REDB изнутри, статья 1: 13 таблиц, на которых работает всё — модель хранения, которую SQLite-провайдеру пришлось воспроизвести один-в-один
- REDB: индексы, или почему на любую схему — это быстро — про индексы
Самое короткое объяснение
services.AddRedb(options => options
.UseSqlite("Data Source=app.db"));
Всё. Дальше redb.Query<Employee>().Where(e => e.Salary > 80_000).OrderBy(e => e.HireDate).ToListAsync(), деревья, группировки, оконные функции, словари, List<ListItem>, soft-delete — тот же самый код, что на Postgres или MSSql. Провайдер меняется одной строкой в DI; приложение не знает, на какой базе оно работает. Ровно ради этого и существует DB-агностичное ядро — и SQLite теперь в нём полноценный гражданин.
Два тира, и тот, что важен для Blazor и мобилок
У RedBase всегда были тиры Free и Pro. Для SQLite граница ложится туда, куда и должна:
SQLite Pro — чистый C#, ни строчки нативного кода. SQL запроса генерится в C# (ProSqlBuilder), материализация props — тоже в C#. Он не вызывает ни одной серверной функции БД. А значит — работает везде, где работает Microsoft.Data.Sqlite: в том числе в Blazor WebAssembly и на мобилках (MAUI / iOS / Android), где нативное расширение SQLite загрузить нельзя в принципе. Это и есть тот тир, который у нас просили: типизированное LINQ-хранилище в одном файле прямо внутри браузера или приложения.
Pro — бесплатный. Нужно только зарегистрироваться на redb.ru и получить лицензионный ключ — инструкция по подключению там же, сразу после регистрации. Тир для WASM/мобилок не стоит денег; регистрация — просто способ выдать ключ.
SQLite Free — нативное загружаемое расширение. Прямой аналог того, как Free устроен на Postgres/MSSql: вся тяжёлая машинерия (материализатор get_object_json, компилятор запросов) живёт внутри БД как in-DB SQL-функции. На SQLite это нативное C-расширение (redb.dll / .so / .dylib), грузится на каждое соединение. Работает везде, где грузится нативный код — десктоп, сервер, CI — и это же то, что дёрнет не-.NET хост (Python, sqlite3 CLI), чтобы говорить с базой RedBase напрямую.
Итого: WASM и мобилки → Pro; десктоп/сервер/Python → любой. Разница между Free и Pro — в материализации и change-tracking, а не в том, какой запрос вы получаете: форма запроса у обоих тиров одна и та же.
Чего стоило собрать Free-расширение
Самое интересное. На Postgres и MSSql «Free» — это ворох серверных SQL-функций (PL/pgSQL, T-SQL): движок запросов v2-pvt, get_object_json, save_object_json, soft-delete, view прав. У SQLite процедурного языка нет вообще. Поэтому под Free мы портировали весь этот движок в нативное C-расширение поверх sqlite3ext.h:
get_object_json— рекурсивный материализатор: base-поля, скаляры, массивы и словари (реляционно через_array_index/_array_parent_id), вложенныеClass-поля, ссылки на объекты,ListItem(включаяListItem, который сам несётObject). Читает_valuesи собирает JSON, который десериализует C#-слой.- Компилятор
pvt_*— ~9 тысяч строк логики PL/pgSQL, заново выраженных на C как генерация SQL-строк:pvt_build_query_sql,pvt_build_aggregate_sql,pvt_build_groupby_sql,pvt_build_window_sql,pvt_build_projection_sql,pvt_build_array_groupby_sql. Postgres-измы переезжают в SQLite:array_agg … FILTER→json_group_array … FILTER,= ANY(arr)→IN (SELECT value FROM json_each(...)),EXTRACT(… FROM x)→CAST(strftime(...) AS INTEGER),ILIKE→LIKEи так далее. - Идентификаторы — последовательностей в SQLite нет, поэтому генератор ключей использует нативную
AUTOINCREMENT-таблицу и резервирует блоки id изsqlite_sequence; C-расширение и C#-генератор двигают один и тот же счётчик, так что id остаются глобально уникальными — хоть из .NET, хоть из Python-хоста. - Soft-delete и права —
mark_for_deletion/purge_trashкак нативный мульти-стейтмент SQL, и viewv_user_permissions(рекурсивное наследование прав по дереву + глобальный fallback) для эффективных прав.
Оба тира проходят полный набор примеров (145/145) — тот же, что гейтит Postgres и MSSql.
Минимальная версия SQLite — 3.44.0+ (ноябрь 2023): мы опираемся на FILTER (WHERE …), современные оконные функции, RETURNING, JSON1 и рекурсивные CTE, чтобы SQLite-SQL оставался близок к Postgres-SQL, а не превращался в переписывание с нуля.
С чего начать
Free (десктоп/сервер — нативное расширение):
services.AddRedb(options => options
.UseSqlite("Data Source=app.db"));
// путь к нативному расширению — через env REDB_SQLITE_EXTENSION,
// либо резолвится redb.{dll,so,dylib} из runtimes-папки пакета
Pro (Blazor WASM / мобилки / где угодно — чистый C#):
services.AddRedbPro(options => options
.WithLicense(licenseKey)
.UseSqlite("Data Source=app.db"));
Одна деталь — общая для обоих: UseSqlite тир-агностичен. AddRedb даёт Free, AddRedbPro — Pro, и тир переключается без правки using-ов. Дальше — тот LINQ, который вы и так пишете.
Что честно ещё не доделано
В серии принято говорить и про то, что не готово:
- Кросс-платформенные нативные бинарники Free. C-расширение сейчас собрано под Windows x64. Linux (
.so) и macOS (.dylib, включая arm64) собираются из того же CMake-проекта — CI-матрица и per-RID упаковка в NuGet (runtimes/<rid>/native/) следующие в списке. У Pro нативной зависимости нет — он уже сегодня работает везде, и это ровно то, что нужно WASM/мобилкам. - In-memory базы работают, но
:memory:в SQLite — per-connection: нуженMode=Memory;Cache=Sharedплюс один удерживаемый коннект, чтобы пул видел одну и ту же базу. Это особенность жизненного цикла SQLite, не RedBase. - Точность decimal.
NUMERICпо умолчанию маппится вREAL(быстро, но лоссово за пределами double); точный вариант черезTEXT— запланированная настройка. Известное слабое место SQLite. - Скорость Free-запросов — это модель серверных функций: для embedded/локальных нагрузок ок, но на больших выборках по сырой пропускной способности C#-материализация Pro быстрее (by design — это и есть разница тиров).
Почему это тот релиз, который хотелось выпустить
RedBase начинался как «альтернатива EF Core для сложных графов объектов на Postgres/MSSql». В 3.2.0 тот же движок — тот же LINQ, та же модель без миграций, те же деревья и проекции — крутится во вкладке браузера и на телефоне, из одного файла, офлайн. Это не порт; это DB-агностичное ядро, наконец доехавшее туда, куда его чаще всего и просили.
Репозиторий, доки и пакеты — redbase.app. Вопросы, «а делает ли оно X на SQLite» и баг-репорты — несите, провайдер совсем новый, обратная связь открыта.