-
Notifications
You must be signed in to change notification settings - Fork 0
feat: add persistence docs #59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,215 @@ | ||
| ## Itmo.Dev.Platform.Persistence | ||
|
|
||
| Библиотека для работы с базами данных. Включает в себя абстракции для подключения к БД, выполнения запросов, управления транзакциями и миграциями. | ||
|
|
||
| ## Подключение | ||
|
|
||
| Библиотека состоит из двух Nuget пакетов: `Itmo.Dev.Platform.Persistence.Abstractions` с абстракциями и | ||
| `Itmo.Dev.Platform.Persistence.Postgres` с реализациями для БД Postgres. | ||
|
|
||
| Для регистрации в DI-контейнере необходимо вызвать метод `AddPlatformPersistence`, предварительно зарегистрировав саму | ||
| платформу (метод `AddPlatform`). | ||
| Метод принимает делегат, в котором происходит конфигурация Persistence слоя. | ||
|
|
||
| ```csharp | ||
| collection.AddPlatformPersistence(persistence => persistence.UsePostgres(postgres => postgres | ||
| .WithConnectionOptions(static builder => builder | ||
| .BindConfiguration("Infrastructure:Persistence:Postgres")) | ||
| .WithMigrationsFrom(typeof(IAssemblyMarker).Assembly) | ||
| .WithDataSourcePlugin<MappingPlugin>())); | ||
| ``` | ||
|
|
||
| У конфигуратора есть метод расширения `UsePostgres`, который настраивает подключение к PostgreSql. В метод так же передаётся | ||
| делегат, в котором можно параметризовать работу с базой с помощью методов: | ||
|
|
||
| - `WithConnectionOptions` - настройка конфигурации подключения к БД. | ||
| - `WithMigrationsFrom` - указание сборки(-ок) с миграциями. Также можно использовать `WithMigrationsFromItems` для указания конкретных типов | ||
| с миграциями. | ||
| - `WithDataSourcePlugin` - подключение плагинов для `NpgsqlDataSource`. Используемые плагины | ||
| должны реализовывать `IPostgresDataSourcePlugin`, и фактически позволяют дополнительно настроить `NpgsqlDataSourceBuilder` | ||
| перед созданием data source. | ||
|
|
||
| ## Конфигурация | ||
|
|
||
| Параметры подключения к БД задаются через `PostgresConnectionOptions`. | ||
|
|
||
| ### Схема конфигурации | ||
|
|
||
| ```json | ||
| { | ||
| "Host": string, | ||
| "Port": int, | ||
| "Database": string, | ||
| "Username": string, | ||
| "Password": string, | ||
| "SslMode": string, | ||
| "Pooling": bool, | ||
| "MaximumPoolSize": int, | ||
| "EnableConnectionProviderLogging": bool | ||
| } | ||
| ``` | ||
|
|
||
| - **Host** | ||
| Адрес хоста PostgreSql. Обязательный параметр, не может быть пустым. | ||
| - **Port** | ||
| Порт PostgreSql. Обязательный параметр, не может быть пустым или равным 0. Допустимые значения: `1`-`65535`. | ||
| - **Database** | ||
| Название базы данных. Обязательный параметр, не может быть пустым. | ||
| - **Username** | ||
| Имя пользователя. Обязательный параметр, не может быть пустым. | ||
| - **Password** | ||
| Пароль. Обязательный параметр, не может быть пустым. | ||
| - **SslMode** | ||
| Режим SSL-соединения. Необязательный параметр, по умолчанию пустая строка. | ||
| Допустимые значения аналогичны Npgsql ([документация](https://www.npgsql.org/doc/api/Npgsql.SslMode.html)): `Disable`, `Allow`, `Prefer`, `Require`, `VerifyCA`, `VerifyFull`. | ||
| - **Pooling** | ||
| Использование пула соединений (connection pooling). Необязательный параметр, по умолчанию `true`. | ||
| - **MaximumPoolSize** | ||
| Максимальный размер пула соединений. Необязательный параметр, по умолчанию `10`, должен быть >= `1`. | ||
| - **EnableConnectionProviderLogging** | ||
| Включение логирования. Необязательный параметр, по умолчанию `false`. | ||
| Для корректной работы необходимо также добавить платфморменный observability - в DI контейнере | ||
| вызвать метод `.AddPlatformObservability` | ||
|
|
||
| > Валидация значений происходит при старте сервиса. При невалидных значениях сервис не запустится. | ||
|
|
||
| ### Configuration example | ||
|
|
||
| ```json | ||
| { | ||
| "Infrastructure": { | ||
| "Persistence": { | ||
| "Postgres": { | ||
| "Host": "localhost", | ||
| "Database": "test-db", | ||
| "Port": 6432, | ||
| "Username": "postgres", | ||
| "Password": "postgres", | ||
| "SslMode": "Prefer", | ||
| "Pooling": true, | ||
| "MaximumPoolSize": 10, | ||
| "EnableConnectionProviderLogging": false | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ## Подключение к БД и выполнение запросов | ||
|
|
||
| ### Получение соединения | ||
|
|
||
| Для получения соединений с БД используется `IPersistenceConnectionProvider`. с БД используется `IPersistenceConnectionProvider`. | ||
| Провайдер зарегистрирован в DI как Scoped, поэтому сервисы использующие его должны быть зарегистрированы как Scoped или Transient. | ||
|
|
||
| Чтобы получить объект соединения, необходимо вызвать метод провайдера `GetConnectionAsync`, который возвращает | ||
| `IPersistenceConnection`. | ||
|
|
||
| ```csharp | ||
| await using IPersistenceConnection connection = await _connectionProvider.GetConnectionAsync(cancellationToken); | ||
| ``` | ||
|
|
||
| ### Создание и настройка команды | ||
|
|
||
| Команда создается через метод `CreateCommand` на объекте соединения. Параметры добавляются в fluent стиле: | ||
|
|
||
| ```csharp | ||
| await using IPersistenceCommand command = connection.CreateCommand(sql) | ||
| .AddParameter("parameter_1", value1) | ||
| .AddParameter("parameter_2", value2); | ||
| ``` | ||
|
|
||
| Помимо вышеуказанного `AddParameter<T>(string parameterName, T value)`, у `IPersistenceConnection` существуют другие методы и перегрузки для добавления параметров: | ||
|
|
||
| - `AddParameter(DbParameter parameter)` - добавляет сырой `DbParameter`, фактически параметр должен быть типа `NpgsqlParameter`. | ||
| - `AddParameter<T>(string parameterName, T value)` - добавляет именованный параметр произвольного типа. | ||
| - `AddParameter<T>(string parameterName, IEnumerable<T> values)` - добавляет именованный параметр-список. | ||
| Несмотря на ограничение Npgsql в передаваемом типе коллекции для параметра (`List<T>` или `T[]`), в данный метод можно передавать | ||
| любой тип, реализующий `IEnumerable<T>`. В sql коде удобно подставлять в `= any(...)` или `in (...)`. | ||
| - `AddJsonParameter<T>(string parameterName, T value, JsonSerializerSettings? serializerSettings = null)` - сериализует переданное значение в json и добавляет как параметр с типом `jsonb`. | ||
| - `AddNullableJsonParameter<T>(string parameterName, T? value, JsonSerializerSettings? serializerSettings = null)` - аналогично `AddJsonParameter`, но поддерживает `null` значения. | ||
| - `AddJsonArrayParameter<T>(string parameterName, IEnumerable<T> values, JsonSerializerSettings? serializerSettings = null)` - сериализует каждый элемент коллекции в json и добавляет как параметр с типом `jsonb[]`. | ||
| - `AddJsonArrayParameter(string parameterName, IEnumerable<string> values)` - добавляет коллекцию строк (уже сериализованных объектов) как параметр с типом `jsonb[]`. | ||
| - `AddMultiArrayStringParameter<T>(string parameterName, IEnumerable<IEnumerable<T>> values)` - позволяет добавлять многомерные массивы в качестве параметров. | ||
| Каждый элемент коллекции 1-го уровня преобразуется в postgres-массив, затем итоговый список добавляется как параметр с типом `text[]`. | ||
| Для корректной работы в sql, необходимо явно привести каждый элемент списка к ожидаемому postgres-типу (например, `integer[]` или `bigint[]`). | ||
| - `AddMultiArrayStringParameter(string parameterName, IEnumerable<IEnumerable<string>> values)` - аналогично `AddMultiArrayStringParameter`, но для строковых значений коллекции. | ||
|
|
||
| > При попытке добавить параметр с уже существующим названием будет выброшен `PlatformPersistencePostgresException`. | ||
|
|
||
| ### Выполнение запросов | ||
|
|
||
| Чтение данных реализовано через метод команды `ExecuteReaderAsync`, аналогично `NpgsqlCommand`: | ||
|
|
||
| ```csharp | ||
| await using DbDataReader reader = await command.ExecuteReaderAsync(cancellationToken); | ||
|
|
||
| while (await reader.ReadAsync(cancellationToken)) | ||
| { | ||
| yield return new Model( | ||
| Id: reader.GetInt64("field_1"), | ||
| Name: reader.GetString("field_2")); | ||
| } | ||
| ``` | ||
|
|
||
| Выполнение не читающих запросов (DML) реализовано через метод `ExecuteNonQueryAsync`, так же аналогично `NpgsqlCommand`: | ||
|
|
||
| ```csharp | ||
| await command.ExecuteNonQueryAsync(cancellationToken); | ||
| ``` | ||
|
|
||
| ## Транзакции | ||
|
|
||
| Для управления транзакциями используется `IPersistenceTransactionProvider`, зарегистрированный в DI как Scoped. | ||
| Соответственно сервисы, использующие его, должны быть зарегистрированы как Scoped или Transient. | ||
|
|
||
| Для открытия транзакции используется метод `BeginTransactionAsync` с указанием уровня изоляции. | ||
| Метод возвращает `IPersistenceTransaction` - объект транзакции, который после выполнения операций необходимо закоммитить | ||
| (вызвать метод `CommitAsync`). Все операции, выполненные до вызова этого метода, автоматически попадают в транзакцию. | ||
|
|
||
| Если `CommitAsync` не был вызван, транзакция откатывается при `DisposeAsync`. | ||
TGontar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| ```csharp | ||
| await using IPersistenceTransaction transaction = await _transactionProvider | ||
| .BeginTransactionAsync(IsolationLevel.ReadCommitted, cancellationToken); | ||
|
|
||
| // выполнение операций через репозитории | ||
|
|
||
| await transaction.CommitAsync(cancellationToken); | ||
| ``` | ||
|
|
||
| Фактически реализация `IPersistenceTransaction` использует под капотом `TransactionScope` с указанием параметра scopeOption: `TransactionScopeOption.Required`, | ||
| тем самым представляя собой ту же ambient транзакцию. | ||
|
|
||
| ## Миграции | ||
|
|
||
| Механизм миграций реализован на базе библиотеки FluentMigrator. | ||
| Для написания миграции необходимо наследовать абстрактный класс `SqlMigration`, в котором определены методы `GetUpSql` и `GetUpSql`. | ||
|
|
||
| `GetUpSql` - sql-скрипт самой миграции, `GetDownSql` - скрипт отката | ||
|
|
||
| ```csharp | ||
| [Migration(1, "Initial")] | ||
| public class Initial : SqlMigration | ||
| { | ||
| protected override string GetUpSql(IServiceProvider serviceProvider) | ||
| { | ||
| return """ | ||
| create table example | ||
| ( | ||
| id bigserial not null primary key, | ||
| name text not null | ||
| ); | ||
| """; | ||
| } | ||
|
|
||
| protected override string GetDownSql(IServiceProvider serviceProvider) | ||
| { | ||
| return """ | ||
| drop table example; | ||
| """; | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Миграции запускаются автоматически при старте приложения. Сборка с миграциями указывается при регистрации в DI, методе `WithMigrationsFrom`. | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,3 @@ | ||
| # Itmo.Dev.Platform.Persistence.Abstractions | ||
| # Itmo.Dev.Platform.Persistence.Abstractions | ||
|
|
||
| Документация: [README.md](https://github.com/itmo-is-dev/platform/blob/master/docs/persistence.md) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,3 @@ | ||
| # Itmo.Dev.Platform.Persistence.Postgres | ||
| # Itmo.Dev.Platform.Persistence.Postgres | ||
|
|
||
| Документация: [README.md](https://github.com/itmo-is-dev/platform/blob/master/docs/persistence.md) |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.