Сохранение данных с помощью поставщика EF Core Azure Cosmos DB

Основы сохранения

Сохранение данных с помощью поставщика Azure Cosmos DB аналогично другим поставщикам. Вы добавляете, изменяете или удаляете сущности, а затем вызываете SaveChangesAsync сохранение этих изменений в базе данных. Дополнительные сведения об основах сохранения см. в разделе "Сохранение данных".

Транзакционность и пакеты транзакций

Замечание

В EF Core 11 реализована поддержка пакетных пакетов транзакций, которая в настоящее время находится в предварительной версии.

Azure Cosmos DB обеспечивает ограниченную поддержку атомарных транзакций; Это общее ограничение баз данных документов, где основное внимание уделяется масштабируемости и доступности, а не строгой семантике транзакций. Azure Cosmos DB поддерживает пакеты транзакций, которые позволяют выполнять операции вместе как пакет в одной секции (см. дополнительные ограничения ниже). Атомарность гарантируется в рамках одного пакета: если любая из операций завершается ошибкой, весь пакет откатывается, и ни одно из его изменений не будет применено. Однако после записи пакета его нельзя откатить или отложить, а атомарность не может быть применена в нескольких пакетах. Пакеты транзакций также обеспечивают преимущество производительности, так как несколько документов можно обновлять в одном цикле, а не выполнять циклический обход для каждого обновления.

Начиная с EF 11, поставщик EF Core Azure Cosmos DB по умолчанию использует транзакционные пакеты, обеспечивая максимальное приближение атомарности (и оптимальную производительность) при сохранении изменений. Поведение пакетной обработки может контролироваться свойством AutoTransactionBehavior, что позволяет разработчикам балансировать между производительностью, гарантиями согласованности и поведением при сбоях в зависимости от потребностей приложения.

  • Авто (по умолчанию) — EF автоматически группирует операции в транзакционные пакеты, которые выполняются последовательно. Если пакет завершается ошибкой, выполнение останавливается немедленно, а последующие изменения не сохраняются, а все ранее успешные пакеты остаются зафиксированными. Обычно это обеспечивает хорошую производительность при выполнении нескольких операций, которые можно сгруппировать по значению ключа раздела, с максимально возможным приближением к атомарности.
  • Никогда — все операции выполняются по отдельности и последовательно, в точном порядке их отслеживания. Это позволяет избежать пакетной обработки и может быть медленнее, особенно для большого количества изменений. Это было поведение до версии 11.
  • Всегда — требуется, чтобы все операции могли выполняться как один пакет транзакций; Если любая операция не может быть включена в пакет (см. ограничения), создается исключение. Это позволяет гарантировать полную атомарность (и один циклический обход) при выполнении SaveChangesAsync, но после этого необходимо вручную убедиться, что все операции могут выполняться в пакете транзакций.

Ниже приведен пример использования Always, который приводит SaveChangesAsync к сбою, так как выполняется попытка выполнить слишком много операций:

using var context = new BlogsContext();
context.Database.AutoTransactionBehavior = AutoTransactionBehavior.Always;
context.AddRange(Enumerable.Range(0, 101).Select(i => new Post())); // 101 entries exceeds the batch item limit of 100.
await context.SaveChangesAsync(); // Throws InvalidOperationException since the changes cannot be saved in a single batch.

Ограничения

  • Пакеты транзакций могут выполняться только в одной секции.
  • Пакеты транзакций могут содержать только до 100 операций и не могут превышать 2 МБ данных в общей сложности.
  • Azure Cosmos DB не разрешает записи документов с пред- или пост-триггерами быть частью пакета транзакций. Из-за этого все объекты, настроенные с триггерами, выполняются отдельно и перед любыми транзакционными пакетами. Это может повлиять на порядок и согласованность в смешанных сценариях.

Массовое выполнение

Замечание

Массовое выполнение вводится в EF Core 11, которая в настоящее время находится в предварительной версии.

До версии 11 поставщик Azure Cosmos DB последовательно выполнял операции документов при вызове SaveChangesAsync; при сохранении большого количества сущностей это было медленно, так как каждая операция ожидает завершения предыдущей операции перед началом работы. Версия 11 поддерживает транзакционные пакеты, которые позволяют группировать операции для повышения производительности. Однако пакеты транзакций могут использоваться только для одной секции и имеют различные ограничения (см. документацию выше).

Альтернативный подход — использование Azure Cosmos DB поддерживает массовое выполнение, что позволяет выполнять несколько операций документа параллельно и между экземплярами DbContext, что значительно повышает пропускную способность при сохранении нескольких сущностей одновременно. Это особенно полезно для сценариев загрузки данных, пакетных операций или любой ситуации, в которой необходимо сохранить множество сущностей.

Чтобы включить массовое выполнение, настройте контекст с помощью BulkExecutionEnabled() параметра:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.UseCosmos(
        "https://localhost:8081",
        "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
        databaseName: "OrdersDB",
        options => options.BulkExecutionEnabled());

При включенном массовом выполнении операции записи документов выполняются параллельно, а не последовательно. Это может обеспечить значительные улучшения производительности при сохранении нескольких сущностей, особенно в сценариях, включающих сотни или тысячи документов. Однако перед включением этого не забудьте понять предостережения . Обратите внимание, что поведение SaveChangesAsync остается неизменным с точки зрения API; только внутреннее выполнение меняется, чтобы задействовать массовые возможности Azure Cosmos DB.

Дополнительные сведения о массовом выполнении в Azure Cosmos DB см. в следующей документации по Azure:

Управление токенами сеанса

Замечание

В EF Core 11 реализовано управление маркерами сеансов, которое в настоящее время находится в предварительной версии.

Azure Cosmos DB использует маркеры сеансов для отслеживания согласованности операций чтения и записи в сеансе. При использовании согласованности сеансов (по умолчанию) каждая операция чтения или записи возвращает маркер сеанса, который можно использовать в последующих запросах, чтобы убедиться, что запрашивающий клиент считывает свои собственные записи.

По умолчанию набор SDK для .NET Azure Cosmos DB автоматически управляет токенами сессии в одном CosmosClient экземпляре. Однако в некоторых сценариях может потребоваться вручную управлять маркерами сеанса:

  • Когда приложение использует подсистему балансировки нагрузки с циклическим перебором, которая не поддерживает сходство сеансов между HTTP-запросами
  • Когда маркеры сеанса необходимо сохранить и восстановить во время перезапуска приложения
  • Когда токены сеанса необходимо использовать между различными DbContext экземплярами или процессами приложения

Настройка режима управления маркерами сеанса

Чтобы включить управление маркерами сеанса вручную, настройте SessionTokenManagementMode() при настройке контекста:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder.UseCosmos(
        "<connection string>",
        databaseName: "OrdersDB",
        options => options.SessionTokenManagementMode(SessionTokenManagementMode.SemiAutomatic));

Доступны перечисленные ниже режимы.

Mode Description
FullyAutomatic Режим по умолчанию. Использует основной механизм автоматического управления токенами сеанса библиотеки SDK Cosmos DB. GetSessionTokens и UseSessionTokens методы вызываются при вызове.
SemiAutomatic Разрешает перезапись автоматического управления маркерами сеанса пакета SDK с помощью UseSessionTokens. Если UseSessionTokens для контейнера не был вызван, используется автоматическое управление SDK. EF отслеживает маркеры сеансов, которые можно получить с помощью GetSessionTokens.
Manual Полностью заменяет автоматическое управление токенами сеанса SDK. Используются только маркеры сеансов, указанные через UseSessionTokens или отслеживаемые операциями EF.
EnforcedManual То же самое, что и Manual, но выбрасывает исключение, если UseSessionTokens не был вызван для контейнера перед выполнением операции на нем.

Получение токенов сеанса

После выполнения операций чтения или записи можно получить маркеры сеанса, отслеживаемые EF:

// Get the session token for the default container
string? sessionToken = context.Database.GetSessionToken();

// Get session tokens for all containers
IReadOnlyDictionary<string, string?> allTokens = context.Database.GetSessionTokens();

Настройка токенов сеанса

Перед выполнением операций чтения можно задать маркеры сеанса, чтобы убедиться, что вы читаете собственные записи:

// Set the session token for the default container
context.Database.UseSessionToken(sessionToken);

// Set session tokens for all containers
context.Database.UseSessionTokens(sessionTokens);

Добавление токенов сессии

Вы также можете добавить маркеры сеанса в существующие. Это полезно при наличии маркеров сеанса из нескольких источников и хотите их объединить:

// Append a session token for the default container
context.Database.AppendSessionToken(sessionToken);

// Append session tokens for the specified containers
context.Database.AppendSessionTokens(sessionTokens);

Пример. Управление маркерами сеанса в веб-приложении с балансировкой нагрузки

В следующем примере показано, как управлять сессионными токенами в веб-приложении с несколькими экземплярами за балансировщиком нагрузки round-robin:

// After writing data, retrieve and store the session token (e.g., in a cookie or header)
public async Task<IActionResult> CreateDocument(DocumentDto dto)
{
    await using var context = new MyCosmosContext();

    var document = new Document { Name = dto.Name };
    context.Documents.Add(document);
    await context.SaveChangesAsync();

    // Retrieve the session token to return to the client
    var sessionToken = context.Database.GetSessionToken();

    // Store in response header for the client to send back in subsequent requests
    Response.Headers["x-cosmos-session-token"] = sessionToken;

    return Ok(document.Id);
}

// Before reading data, apply the session token from the client
public async Task<IActionResult> GetDocument(int id)
{
    await using var context = new MyCosmosContext();

    // Get the session token from the request header
    if (Request.Headers.TryGetValue("x-cosmos-session-token", out var sessionToken))
    {
        context.Database.UseSessionToken(sessionToken!);
    }

    var document = await context.Documents.FindAsync(id);
    return document is not null ? Ok(document) : NotFound();
}

Предупреждение

Управление маркерами сеанса вручную может нарушить согласованность сеансов при неправильной обработке. Используйте ручной режим только при наличии определенных потребностей, таких как работа за балансировщиком нагрузки без привязки сеансов. Дополнительные сведения см. в разделе "Использование маркеров сеансов " в документации по Azure Cosmos DB.