Ключевые особенности
| Язык | C# |
| Фреймворк | .NET Standard 2.1 / .NET 6 / .NET 8 / .NET 9 / .NET 10 |
| Тип проекта | Библиотека |
| Статус | Готова к использованию |
| Лицензия | MIT |
| Nuget | |
| Загрузки | |
| Github | Laraue.EfCoreTriggers |
Что такое триггеры?
Триггеры базы данных — это специальные процедуры, которые автоматически выполняются в ответ на определенные события базы данных, такие как вставка, обновление или удаление записей. Они используются для обеспечения соблюдения бизнес-правил, поддержания целостности данных, логирования и так далее.
Когда использование триггеров оправдано
В современной разработке приложений разработчики часто стараются избегать триггеров базы данных, предпочитая реализовывать логику на уровне приложения. У этого подхода есть преимущества:
- Улучшенная масштабируемость приложения: База данных часто является узким местом в приложениях. Дополнительная логика, выполняемая внутри базы данных, потребляет ресурсы. Масштабирование базы данных обычно вертикальное (увеличение мощности одного сервера), в то время как хосты приложений и инфраструктура между ними часто масштабируются более эффективно.
- Централизованная логика приложения: Хранение логики в одном месте упрощает ее понимание, сопровождение и отладку.
Однако триггеры по-прежнему имеют преимущества, которые оправдывают их использование в определенных ситуациях:
- Упрощение модификации устаревших систем: Добавление репликации в устаревшие системы может потребовать значительных изменений кода. Когда кодовая база плохо структурирована или не имеет всеобъемлющих тестов, такие изменения могут легко привести к ошибкам и непредвиденным последствиям.
- Обеспечение целостности данных: В некоторых средах пользователи могут иметь прямой доступ к базе данных. Это может нарушить целостность данных, если изменения не отслеживаются на уровне приложения. Триггеры могут обеспечить дополнительный уровень защиты.
- Экономически эффективная инфраструктура: Небольшие организации часто стремятся минимизировать затраты на инфраструктуру. Для более простых случаев использования может быть более экономично использовать существующую базу данных и построить систему событий внутри нее, вместо управления выделенной системой обмена сообщениями, такой как Kafka или RabbitMQ.
Проблема с триггерами в EF Core
При работе с триггерами и EF Core рекомендованным образом, необходимо использовать SQL для создания триггера в миграциях:
migrationBuilder.Sql("CREATE TRIGGER ...")Хотя это работает, управление триггерами становится сложным:
- Отсутствие видимости: Сложно определить, существует ли триггер, не просматривая файлы миграций или не проверяя базу данных напрямую.
- Сложности сопровождения: Когда модель сущности, связанная с триггером, изменяется, триггер также должен быть обновлен. Невыполнение этого требования приведет к исключениям во время выполнения.
Видение библиотеки
Эта библиотека направлена на решение этих проблем. Позволяя определять триггеры с использованием плавного синтаксиса, аналогично тому, как определяются индексы и внешние ключи, библиотека будет:
- Обеспечивать проверку во время компиляции для обнаружения ошибок при изменении моделей.
- Автоматически пересоздавать триггеры при необходимости.
- Четко связывать триггеры с соответствующими сущностями в определении модели.
Как использовать
Определите необходимый триггер в построителе модели:
modelBuilder.Entity<Transaction>()
.AfterUpdate(trigger => trigger
.Action(action => action
.Condition(tableRefs => tableRefs.Old.IsVeryfied && tableRefs.New.IsVeryfied) // Триггер выполнится только при выполнении условия
.Update<UserBalance>(
(tableRefs, userBalances) => userBalances.UserId == tableRefs.Old.UserId, // Условие, какие записи обновятся
(tableRefs, oldBalance) => new UserBalance { Balance = oldBalance.Balance + tableRefs.New.Value - tableRefs.Old.Value }))); // Какими значениями обновлять записи, попавшие под условияТеперь добавьте функциональность триггеров и соответствующий провайдер базы данных в DbContextOptionsBuilder
var options = new DbContextOptionsBuilder<TestDbContext>()
.UseNpgsql("User ID=test;Password=test;Host=localhost;Port=5432;Database=test;")
.UsePostgreSqlTriggers() // Select the package for your database, Postgres provider in the example
.Options;
var dbContext = new TestDbContext(options);Сгенерируйте миграцию. SQL для создания триггера будет добавлен в секции Up и Down созданной миграции.
Сложности
Конкретные проблемы и решения будут описаны в отдельных статьях:
- Разбор деревьев выражений
- Поддержка различных провайдеров баз данных
Хронология
- Ноябрь 2020: Первоначальная версия с поддержкой Postgres.
- Декабрь 2020: Добавлена поддержка MS SQL, SQLite, MySQL.
- ~ Декабрь 2022: Стабильная версия с обширной поддержкой встроенных математических и строковых функций.
- Декабрь 2025: Базовая функциональность триггеров вынесена в отдельные пакеты. Это позволяет генерировать триггеры без EF Core и упрощает обновление версий.
Дальнейшие улучшения
- Рефакторинг системы плагинов для перевода методов, чтобы привести ее в соответствие с подходом, используемым в Linq2DB: помечать методы атрибутами для определения того, как они должны транслироваться в SQL.