Приложение на C# (.NET)

На этой странице подробно разбирается код тестового приложения, использующего C# (.NET) SDK YDB.

Примечание

Материал статьи дополняется.

Инициализация соединения с базой данных

Для взаимодействия с YDB создается экземпляр драйвера, клиента и сессии:

  • Драйвер YDB отвечает за взаимодействие приложения и YDB на транспортном уровне. Драйвер должен существовать на всем протяжении жизненного цикла работы с YDB и должен быть инициализирован перед созданием клиента и сессии.
  • Клиент YDB работает поверх драйвера YDB и отвечает за работу с сущностями и транзакциями.
  • Сессия YDB содержит информацию о выполняемых транзакциях и подготовленных запросах и содержится в контексте клиента YDB.

Фрагмент кода приложения для инициализации драйвера:

public static async Task Run(
    string endpoint,
    string database,
    ICredentialsProvider credentialsProvider)
{
    var config = new DriverConfig(
        endpoint: endpoint,
        database: database,
        credentials: credentialsProvider
    );

    using var driver = new Driver(
        config: config
    );

    await driver.Initialize();
}

Фрагмент кода приложения для создания сессии:

using var tableClient = new TableClient(driver, new TableClientConfig());

Создание таблиц

Выполняется создание таблиц, которые используются в дальнейших операциях тестового приложения. В результате исполнения шага в базе данных будут созданы таблицы модели данных справочника сериалов:

  • series - Сериалы
  • seasons - Сезоны
  • episodes - Эпизоды

После создания вызывается метод получения информации об объекте схемы данных, и выводится результат его выполнения.

Для создания таблиц используется метод session.ExecuteSchemeQuery с DDL (Data Definition Language) YQL-запросом.

var response = await tableClient.SessionExec(async session =>
{
    return await session.ExecuteSchemeQuery(@"
        CREATE TABLE series (
            series_id Uint64 NOT NULL,
            title Utf8,
            series_info Utf8,
            release_date Date,
            PRIMARY KEY (series_id)
        );

        CREATE TABLE seasons (
            series_id Uint64,
            season_id Uint64,
            title Utf8,
            first_aired Date,
            last_aired Date,
            PRIMARY KEY (series_id, season_id)
        );

        CREATE TABLE episodes (
            series_id Uint64,
            season_id Uint64,
            episode_id Uint64,
            title Utf8,
            air_date Date,
            PRIMARY KEY (series_id, season_id, episode_id)
        );
    ");
});

response.Status.EnsureSuccess();

Запись данных

Выполняется запись данных в созданные таблицы с использованием команды UPSERT языка запросов YQL. Применяется режим передачи запроса на изменение данных с автоматическим подтверждением транзакции в одном запросе к серверу.

Фрагмент кода, демонстрирующий выполнение запроса на запись/изменение данных:

var response = await tableClient.SessionExec(async session =>
{
    var query = @"
        DECLARE $id AS Uint64;
        DECLARE $title AS Utf8;
        DECLARE $release_date AS Date;

        UPSERT INTO series (series_id, title, release_date) VALUES
            ($id, $title, $release_date);
    ";

    return await session.ExecuteDataQuery(
        query: query,
        txControl: TxControl.BeginSerializableRW().Commit(),
        parameters: new Dictionary<string, YdbValue>
            {
                { "$id", YdbValue.MakeUint64(1) },
                { "$title", YdbValue.MakeUtf8("NewTitle") },
                { "$release_date", YdbValue.MakeDate(DateTime.UtcNow) }
            }
    );
});

response.Status.EnsureSuccess();

PRAGMA TablePathPrefix добавляет указанный префикс к путям таблиц внутри БД. Работает по принципу объединения путей в файловой системе — поддерживает ссылки на родительский каталог и не требует добавления слеша справа. Например:

PRAGMA TablePathPrefix = "/cluster/database";
SELECT * FROM episodes;

Подробнее о PRAGMA YQL можно прочитать в документации YQL.

Получение выборки данных

Выполняется запрос на получение выборки данных с использованием команды SELECT языка запросов YQL. Демонстрируется обработка полученной выборки в приложении.

Для выполнения YQL-запросов используется метод session.ExecuteDataQuery(). SDK позволяет в явном виде контролировать выполнение транзакций и настраивать необходимый режим выполнения транзакций с помощью класса TxControl. В фрагменте кода, приведенном ниже, используется транзакция с режимом SerializableRW и автоматическим коммитом после выполнения запроса. Значения параметров запроса передаются в виде словаря имя-значение в аргументе parameters.

var response = await tableClient.SessionExec(async session =>
{
    var query = @"
        DECLARE $id AS Uint64;

        SELECT
            series_id,
            title,
            release_date
        FROM series
        WHERE series_id = $id;
    ";

    return await session.ExecuteDataQuery(
        query: query,
        txControl: TxControl.BeginSerializableRW().Commit(),
        parameters: new Dictionary<string, YdbValue>
            {
                { "$id", YdbValue.MakeUint64(id) }
            },
    );
});

response.Status.EnsureSuccess();
var queryResponse = (ExecuteDataQueryResponse)response;
var resultSet = queryResponse.Result.ResultSets[0];

Обработка результатов выполнения

Результат выполнения запроса (ResultSet) состоит из упорядоченного набора строк (Rows). Пример обработки результата выполнения запроса:

foreach (var row in resultSet.Rows)
{
    Console.WriteLine($"> Series, " +
        $"series_id: {(ulong)row["series_id"]}, " +
        $"title: {(string?)row["title"]}, " +
        $"release_date: {(DateTime?)row["release_date"]}");
}

Скан запросы

Выполняется скан запрос данных, результатом исполнения которого является стрим. Стрим позволяет считать неограниченное количество строк и объем данных.

public void executeScanQuery()
{
  var scanStream = TableClient.ExecuteScanQuery(@$"
    SELECT series_id, season_id, COUNT(*) AS episodes_count
    FROM episodes
    GROUP BY series_id, season_id
    ORDER BY series_id, season_id;
  ");

  while (await scanStream.Next())
  {
    scanStream.Response.EnsureSuccess();

    var resultSet = scanStream.Response.Result.ResultSetPart;
    if (resultSet != null)
    {
      foreach (var row in resultSet.Rows)
      {
        Console.WriteLine($"> ScanQuery, " +
          $"series_id: {(ulong)row["series_id"]}, " +
          $"season_id: {(ulong?)row["season_id"]}, " +
          $"episodes_count: {(ulong)row["episodes_count"]}");
      }
    }
  }
}