redb.Core
redb.Core

Анонс четвёртой версии redb.Core: ссылки грузятся по обращению, уникальность на любом поле, схема обновляется сама. Бесплатно, три СУБД, Free и Pro одинаково.

Готовится redb.Core 4.0. За год это первый мажор такого масштаба: не смена номера, а качественное расширение функционала. Он про три вещи, которые чаще всего просили: ленивая загрузка ссылок между объектами, уникальные ключи на любом поле и обновление схемы базы без ручных действий администратора. Всё вместе, на трёх СУБД сразу и без разделения на «в Free так, в Pro иначе».

Коротко, что изменится для приложения.

Ссылки грузятся тогда, когда их читают

Объект RedbObject<T> умеет ссылаться на другие объекты через свойства Props: заказ ссылается на клиента, клиент на менеджера, менеджер на отдел. До 4.0 один LoadAsync заказа тянул всю эту цепочку на десять уровней вглубь, а единственный способ ограничить аппетит был параметр depth, который просто обрезал ссылки в никуда.

В 4.0 ссылка на границе глубины стала ленивой: у неё есть id, схема и хеш, а Props подгружаются при первом обращении. Ровно этот объект, ровно один запрос. По смыслу это lazy-loading прокси из Entity Framework, только без динамического подкласса: тот же RedbObject<T>, у которого загружены базовые поля и подвешен загрузчик. Его собственные ссылки при этом тоже ленивые, так что ленивость транзитивна и не зависит от того, с какой глубины вы начали.

var order = await redb.LoadAsync<OrderProps>(id, depth: 1);

var customer = order.Props.Customer;   // ленивая ссылка: id, scheme_id, hash
var city = customer.Props.City;        // первое обращение загрузило клиента, и только его

Кому нужен явный контроль, тот помечает ссылку virtual (та же конвенция, что у навигационных свойств EF) и включает EnableLazyReferences: такие ссылки остаются ленивыми на любой глубине, а WithLazyReferences(false) на конкретном запросе возвращает жадную загрузку. Коллекция ленивых ссылок дозагружается одним запросом через LoadReferencesAsync.

Сохранение родителя, вычисление хеша и сериализация в JSON ленивые ссылки не будят. Проверка этих трёх утверждений есть в тестах на каждой из шести конфигураций.

Уникальность на любом поле

Два механизма, для двух разных задач.

ValueUnique у объекта: читаемый строковый ключ (номер заказа, артикул, внешний идентификатор), уникальный в рамках схемы, с поиском по LINQ и SaveByUniqueAsync как upsert по ключу.

[RedbUnique] на поле Props: уникальность значения любого скалярного типа, включая decimal, даты и byte[], обеспеченная индексом базы. Нарушение на всех трёх СУБД поднимается одним типизированным RedbUniqueViolationException, а не тремя разными ошибками драйвера.

public class ProductProps
{
    [RedbUnique] public string Sku { get; set; } = "";
    [RedbUnique] public byte[]? Fingerprint { get; set; }
}

var product = await redb.GetByUniqueAsync<ProductProps>(p => p.Sku, "SKU-1001");

База обновляется сама, а если не может, говорит об этом

4.0 ставится поверх любой базы 3.x. Схема только дополняется: новые колонки с NULL, частичные индексы, новые функции. Ничего не удаляется и не переименовывается, откат на 3.x остаётся возможным.

Обновление применяется при старте приложения. Если у роли приложения нет прав менять схему, старт останавливается типизированным RedbSchemaOutdatedException с указанием версий, а сам скрипт обновления отдаёт GetUpgradeScript() и команда redb schema --upgrade: DBA применяет его сам. Есть и режим AutoApplyDatabaseUpgrades = false для тех, кто хочет держать схему под ручным контролем всегда.

Мелочи, которые заметите

  • byte[] хранится одним BLOB, а не строкой на байт. Старая раскладка конвертируется на первой синхронизации схемы.

  • Схема знает, из какого namespace её тип. Два разных Order из двух проектов больше не могут молча усыновить схему друг друга.

  • В SQLite хеши стали BLOB(16), файлы старого формата конвертируются при открытии.

Что сломается

Убран старый механизм ленивой загрузки Props целиком: EnableLazyLoadingForPropsWithLazyLoading() и параметр lazyLoadProps у LoadAsync. Он делал ленивым дешёвое и жадным дорогое, жил за тремя переключателями и не имел тестов. По умолчанию он был выключен, так что проект без этих настроек ничего не заметит; в остальных достаточно удалить флаг и вызов.

Цифры и условия

Три СУБД (PostgreSQL, MSSQL, SQLite), два издания, шесть тестовых конфигураций, 2304 интеграционных теста зелёные на финальной сборке. Каждая новая проверка доказана красной на непочиненном коде.

Четвёртая версия остаётся бесплатной: Pro-возможности не требуют лицензионного ключа во всей линии 4.x, как и в 3.x. Пакеты redbredb.Routeredb.Tsak и redb.Identity выходят одним номером.

Полный разбор с примерами на каждую возможность выйдет вместе с релизом: redb.ru/articles.

Если было полезно, ⭐ на GitHub поможет другим это найти.

Другие мои статьи — redb.ru/articles, ещё — на Хабре.