Migrar o aplicativo para usar o SDK v3 do .NET do Azure Cosmos DB

APLICA-SE A: NoSQL

Importante

Para saber mais sobre o SDK v3 do .NET do Azure Cosmos DB, veja as Notas sobre a versão, o Repositório GitHub do .NET, as Dicas de desempenho do SDK v3 do .NET e o Guia de solução de problemas.

Este artigo destaca algumas das considerações de atualização de seu aplicativo .NET existente para o SDK v3 do .NET do Azure Cosmos DB mais recente para API para NoSQL. O SDK v3 do .NET do Azure Cosmos DB corresponde ao namespace Microsoft.Azure.Azure Cosmos DB. Você pode usar as informações neste documento se estiver migrando o aplicativo de um dos seguintes SDKs do .NET do Azure Cosmos DB:

  • SDK do .NET Framework v2 do Azure Cosmos DB para API para NoSQL
  • SDK do .NET Core v2 do Azure Cosmos DB para a API para NoSQL

As instruções neste artigo também ajudam você a migrar as seguintes bibliotecas externas que agora fazem parte do SDK do .NET v3 do Azure Cosmos DB para API para NoSQL:

  • Biblioteca do processador do feed de alterações .NET 2.0
  • Biblioteca do executor em massa do .NET 1.1 ou superior

Novidades no SDK v3 do .NET

O SDK v3 contém muitas melhorias de usabilidade e desempenho, incluindo:

  • Nomenclatura do modelo de programação intuitivo
  • .NET Standard 2.0 **
  • Aumento do desempenho por meio do suporte à API de fluxo
  • Hierarquia fluente que substitui a necessidade do alocador de URIs
  • Suporte interno para a biblioteca do processador do feed de alterações
  • Suporte interno para as operações em massa
  • APIs de simulação para teste de unidade mais fácil
  • Lote transacional e suporte Blazor
  • Serializadores conectáveis
  • Dimensionar contêineres não particionados e de dimensionamento automático

** O SDK é direcionado ao .NET Standard 2.0 que unifica os SDKs existentes do .NET Framework e do .NET Core do Azure Cosmos DB em um único SDK do .NET. É possível usar o SDK do .NET em qualquer plataforma que implemente .NET Standard 2.0, incluindo os aplicativos .NET Framework 4.6.1 e superior e .NET Core 2.0 e superior.

A maioria das redes, a lógica de repetição e os níveis inferiores do SDK permanecem, em grande escala, inalterados.

O SDK v3 do .NET do Azure Cosmos DB agora possui código aberto. Saudamos todas as solicitações de pull e serão problemas de registro em log e acompanhamento de comentários GitHub. Trabalharemos na adoção de recursos que melhorem a experiência do cliente.

Por que migrar para o SDK v3 do .NET

Além das inúmeras melhorias de usabilidade e de desempenho, os novos investimentos em recursos feitos no SDK mais recente não serão portados de volta para versões mais antigas. O SDK v2 está atualmente no modo de manutenção. Para obter a melhor experiência de desenvolvimento, recomendamos sempre começar com a versão mais recente com suporte do SDK.

Principais alterações de nome do SDK v2 para o SDK v3

As seguintes alterações de nome foram aplicadas em todo o SDK do .NET 3.0 para alinhar com as convenções de nomenclatura da API para NoSQL:

  • DocumentClient foi renomeado para CosmosClient
  • Collection foi renomeado para Container
  • Document foi renomeado para Item

Todos os objetos de recurso foram renomeados com propriedades adicionais, que incluem o nome do recurso para fins de clareza.

A seguir estão algumas das principais alterações de nome de classe:

SDK v2 do .NET SDK v3 do .NET
Microsoft.Azure.Documents.Client.DocumentClient Microsoft.Azure.Cosmos.CosmosClient
Microsoft.Azure.Documents.Client.ConnectionPolicy Microsoft.Azure.Cosmos.CosmosClientOptions
Microsoft.Azure.Documents.Client.DocumentClientException Microsoft.Azure.Cosmos.CosmosException
Microsoft.Azure.Documents.Client.Database Microsoft.Azure.Cosmos.DatabaseProperties
Microsoft.Azure.Documents.Client.DocumentCollection Microsoft.Azure.Cosmos.ContainerProperties
Microsoft.Azure.Documents.Client.RequestOptions Microsoft.Azure.Cosmos.ItemRequestOptions
Microsoft.Azure.Documents.Client.FeedOptions Microsoft.Azure.Cosmos.QueryRequestOptions
Microsoft.Azure.Documents.Client.StoredProcedure Microsoft.Azure.Cosmos.StoredProcedureProperties
Microsoft.Azure.Documents.Client.Trigger Microsoft.Azure.Cosmos.TriggerProperties
Microsoft.Azure.Documents.SqlQuerySpec Microsoft.Azure.Cosmos.QueryDefinition

Classes substituídas no SDK v3 do .NET

As seguintes classes foram substituídas no SDK 3.0:

  • Microsoft.Azure.Documents.UriFactory

A classe Microsoft.Azure.Documents.UriFactory foi substituída pelo design fluente.

Container container = client.GetContainer(databaseName,containerName);
ItemResponse<SalesOrder> response = await this._container.CreateItemAsync(
        salesOrder,
        new PartitionKey(salesOrder.AccountNumber));

  • Microsoft.Azure.Documents.Document

Como o SDK v3 do .NET permite que os usuários configurem um mecanismo de serialização personalizado, não há nenhuma substituição direta para o tipo Document . Ao usar Newtonsoft.Json (mecanismo de serialização padrão), JObject pode ser usado para obter a mesma funcionalidade. Ao usar um mecanismo de serialização diferente, é possível utilizar o tipo de documento JSON base dele (por exemplo, JsonDocument, para System.Text.Json). A recomendação é usar um tipo C# que reflita o esquema de seus itens em vez de depender de tipos genéricos.

  • Microsoft.Azure.Documents.Resource

Não há substituição direta para Resource. Nos casos em que ele foi usado para documentos, siga as diretrizes para Document.

  • Microsoft.Azure.Documents.AccessCondition

IfNoneMatch ou IfMatch agora estão disponíveis diretamente no Microsoft.Azure.Cosmos.ItemRequestOptions.

Alterações na geração de IDs de item

A ID de item não é mais preenchida automaticamente no SDK v3 do .NET. Portanto, a ID de item deve incluir especificamente uma ID gerada. Veja o seguinte exemplo:

[JsonProperty(PropertyName = "id")]
public Guid Id { get; set; }

Comportamento padrão alterado para o modo de conexão

O SDK v3 agora usa como padrão os modos de conexão Direct + TCP em comparação com o SDK v2 anterior, que tinha como padrão os modos de conexões Gateway + HTTPS. Essa alteração fornece desempenho e escalabilidade aprimoradas.

Alterações no FeedOptions (QueryRequestOptions no SDK v3.0)

A classe FeedOptions no SDK v2 foi renomeada para QueryRequestOptions no SDK v3 e, dentro da classe, várias propriedades tiveram alterações no nome e/ou valor padrão ou foram removidas completamente.

SDK v2 do .NET SDK v3 do .NET
FeedOptions.MaxDegreeOfParallelism QueryRequestOptions.MaxConcurrency - o valor padrão e o comportamento associado continuam iguais, o lado do cliente de operações durante a execução de consulta paralela será executado em série sem paralelismo.
FeedOptions.PartitionKey QueryRequestOptions.PartitionKey – Comportamento mantido.
FeedOptions.EnableCrossPartitionQuery Removidos. O comportamento padrão no SDK 3.0 resulta em consultas entre partições executadas sem a necessidade de habilitar a propriedade especificamente.
FeedOptions.PopulateQueryMetrics Removidos. Agora ele está habilitado por padrão e faz parte do diagnóstico.
FeedOptions.RequestContinuation Removidos. Agora ele está promovido para os próprios métodos de consulta.
FeedOptions.JsonSerializerSettings Removidos. Veja como personalizar a serialização para obter informações adicionais.
FeedOptions.PartitionKeyRangeId Removidos. O mesmo resultado pode ser obtido usando FeedRange como entrada para o método de consulta.
FeedOptions.DisableRUPerMinuteUsage Removidos.

Criar um cliente

O SDK v3 do .NET fornece uma classe CosmosClientBuilder fluente que substitui a necessidade do alocador de URIs do SDK v2.

O design fluente cria URLs internamente e permite que um único objeto Container seja passado em vez de um DocumentClient, DatabaseName e DocumentCollection.

O exemplo a seguir cria um novo CosmosClientBuilder com um ConsistencyLevel forte e uma lista de locais preferenciais:

CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder(
    accountEndpoint: "https://testcosmos.documents.azure.com:443/",
    authKeyOrResourceToken: "SuperSecretKey")
.WithConsistencyLevel(ConsistencyLevel.Strong)
.WithApplicationRegion(Regions.EastUS);
CosmosClient client = cosmosClientBuilder.Build();

Exceções

Onde o SDK v2 usado DocumentClientException para sinalizar erros durante operações, o SDK v3 usa CosmosException, que expõe o StatusCode, Diagnostics e outras informações relacionadas à resposta. Todas as informações completas são serializadas quando ToString() é usado:

catch (CosmosException ex)
{
    HttpStatusCode statusCode = ex.StatusCode;
    CosmosDiagnostics diagnostics = ex.Diagnostics;
    // store diagnostics optionally with diagnostics.ToString();
    // or log the entire error details with ex.ToString();
}

Diagnósticos

Quando o SDK v2 tinha o diagnóstico Somente direto disponível por meio da propriedade RequestDiagnosticsString, o SDK v3 usa Diagnostics disponível em todas as respostas e exceções, que são mais ricas e não restritas ao modo Direto. Eles incluem não apenas o tempo gasto no SDK para a operação, mas também as regiões que a operação contatou:

try
{
    ItemResponse<MyItem> response = await container.ReadItemAsync<MyItem>(
                    partitionKey: new PartitionKey("MyPartitionKey"),
                    id: "MyId");
    
    TimeSpan elapsedTime = response.Diagnostics.GetElapsedTime();
    if (elapsedTime > somePreDefinedThreshold)
    {
        // log response.Diagnostics.ToString();
        IReadOnlyList<(string region, Uri uri)> regions = response.Diagnostics.GetContactedRegions();
    }
}
catch (CosmosException cosmosException) {
    string diagnostics = cosmosException.Diagnostics.ToString();
    
    TimeSpan elapsedTime = cosmosException.Diagnostics.GetElapsedTime();
    
    IReadOnlyList<(string region, Uri uri)> regions = cosmosException.Diagnostics.GetContactedRegions();
    
    // log cosmosException.ToString()
}

ConnectionPolicy

Algumas configurações no ConnectionPolicy foram renomeadas ou substituídas por CosmosClientOptions:

SDK v2 do .NET SDK v3 do .NET
EnableEndpointDiscovery LimitToEndpoint – O valor agora está invertido, se EnableEndpointDiscovery estava sendo definido como true, LimitToEndpoint deve ser definido como false. Antes de usar essa configuração, você precisa entender como ela afeta o cliente.
ConnectionProtocol Removidos. O protocolo está vinculado ao Modo, seja ele Gateway (HTTPS) ou Direto (TCP). Não há mais compatibilidade com o modo direto com o protocolo HTTPS no SDK V3. A recomendação é usar o protocolo TCP.
MediaRequestTimeout Removidos. Não há mais suporte para anexos.
SetCurrentLocation CosmosClientOptions.ApplicationRegion pode ser usado para obter o mesmo efeito.
PreferredLocations CosmosClientOptions.ApplicationPreferredRegions pode ser usado para obter o mesmo efeito.
UserAgentSuffix CosmosClientBuilder.ApplicationName pode ser usado para obter o mesmo efeito.
UseMultipleWriteLocations Removidos. O SDK detecta automaticamente se a conta oferece suporte a vários pontos de extremidade de gravação.

Política de indexação

Na política de indexação, não é possível configurar essas propriedades. Quando não for especificado, essas propriedades terão os seguintes valores padrão:

Nome da Propriedade Novo Valor (não configurável)
Kind range
dataType String e Number

Confira esta seção para obter exemplos de política de indexação para incluir e excluir caminhos. Devido a melhorias no mecanismo de consulta, a configuração dessas propriedades, mesmo se estiver usando uma versão mais antiga do SDK, não tem impacto sobre o desempenho.

Token de sessão

Onde o SDK v2 expôs o token de sessão de uma resposta como ResourceResponse.SessionToken para os casos em que a captura do token de sessão era necessária, porque o token de sessão é um cabeçalho, o SDK v3 expõe esse valor na propriedade Headers.Session de qualquer resposta.

Timestamp

Onde o SDK v2 expunha o carimbo de data/hora de um documento por meio da propriedade Timestamp, porque Document não está mais disponível, os usuários podem mapear a propriedade do sistema _ts para uma propriedade em seu modelo.

OpenAsync

Para casos de uso em que OpenAsync() estava sendo usado para aquecimento do cliente do SDK v2, CreateAndInitializeAsync pode ser usado para OpenAsync() um cliente do SDK v3.

Usar as APIs do processador do feed de alterações diretamente do SDK v3

O SDK v3 tem suporte interno para as APIs do processador do feed de alterações, permitindo que você use o mesmo SDK para criar o aplicativo e a implementação do processador do feed de alterações. Anteriormente, era necessário usar uma biblioteca separada do processador do feed de alterações.

Para obter mais informações, veja como migrar da biblioteca do processador do feed de alterações para o SDK v3 do .NET do Azure Cosmos DB

Consultas do Feed de alterações

A execução de consultas do feed de alterações no SDK v3 é considerada como usando o modelo de pull do feed de alterações. Siga esta tabela para migrar a configuração:

SDK v2 do .NET SDK v3 do .NET
ChangeFeedOptions.PartitionKeyRangeId FeedRange – Para obter paralelismo, é possível usar a leitura do feed de alterações FeedRanges. Não é mais um parâmetro necessário, você pode ler o Feed de Alterações para um contêiner inteiro facilmente agora.
ChangeFeedOptions.PartitionKey FeedRange.FromPartitionKey – Um FeedRange que representa a Chave de Partição desejada pode ser usado para ler o Feed de Alterações para esse valor de Chave de Partição.
ChangeFeedOptions.RequestContinuation ChangeFeedStartFrom.Continuation – O iterador do feed de alterações pode ser interrompido e retomado a qualquer momento salvando a continuação e usando-a ao criar outro iterador.
ChangeFeedOptions.StartTime ChangeFeedStartFrom.Time
ChangeFeedOptions.StartFromBeginning ChangeFeedStartFrom.Beginning
ChangeFeedOptions.MaxItemCount ChangeFeedRequestOptions.PageSizeHint – O iterador do feed de alterações pode ser interrompido e retomado a qualquer momento salvando a continuação e usando-a ao criar outro iterador.
IDocumentQuery.HasMoreResults response.StatusCode == HttpStatusCode.NotModified – O feed de alterações é conceitualmente infinito, portanto, sempre pode haver mais resultados. Quando uma resposta contém o código de status HttpStatusCode.NotModified, significa que não há novas alterações a serem lidas no momento. Você pode usar isso para parar e salvar a continuação ou suspender temporariamente ou aguardar e, em seguida, chamar ReadNextAsync novamente para testar novas alterações.
Tratamento dividido Não é mais necessário que os usuários manipulem exceções divididas ao ler o feed de alterações; as divisões serão tratadas de maneira transparente sem a necessidade de interação do usuário.

Usar a biblioteca do executor em massa diretamente do SDK v3

O SDK v3 tem suporte interno para a biblioteca do executor em massa, permitindo que você use o mesmo SDK para compilar do aplicativo e executar operações em massa. Anteriormente, era necessário usar uma biblioteca do executor em massa separada.

Para obter mais informações, veja como migrar da biblioteca do executor em massa para o suporte em massa no SDK v3 .NET do Azure Cosmos DB

Personalizar a serialização

O SDK V2 do .NET permite definir JsonSerializerSettings em RequestOptions no nível operacional usado para desserializar o documento de resultado:

// .NET V2 SDK
var result = await container.ReplaceDocumentAsync(document, new RequestOptions { JsonSerializerSettings = customSerializerSettings })

O SDK v3 do .NET fornece uma interface do serializador para personalizar totalmente o mecanismo de serialização ou opções de serialização mais genéricas como parte da construção do cliente.

A personalização da serialização no nível da operação pode ser obtida por meio do uso de APIs do Stream:

// .NET V3 SDK
using(Response response = await this.container.ReplaceItemStreamAsync(stream, "itemId", new PartitionKey("itemPartitionKey"))
{

    using(Stream stream = response.ContentStream)
    {
        using (StreamReader streamReader = new StreamReader(stream))
        {
            // Read the stream and do dynamic deserialization based on type with a custom Serializer
        }
    }
}

Comparações de trecho de código

O snippet de código a seguir mostra as diferenças em como os recursos são criados entre os SDKs v2 e v3 do .NET:

Operações de banco de dados

Criar um banco de dados

// Create database with no shared provisioned throughput
DatabaseResponse databaseResponse = await client.CreateDatabaseIfNotExistsAsync(DatabaseName);
Database database = databaseResponse;
DatabaseProperties databaseProperties = databaseResponse;

// Create a database with a shared manual provisioned throughput
string databaseIdManual = new string(DatabaseName + "_SharedManualThroughput");
database = await client.CreateDatabaseIfNotExistsAsync(databaseIdManual, ThroughputProperties.CreateManualThroughput(400));

// Create a database with shared autoscale provisioned throughput
string databaseIdAutoscale = new string(DatabaseName + "_SharedAutoscaleThroughput");
database = await client.CreateDatabaseIfNotExistsAsync(databaseIdAutoscale, ThroughputProperties.CreateAutoscaleThroughput(4000));

Ler um banco de dados pela ID

// Read a database
Console.WriteLine($"{Environment.NewLine} Read database resource: {DatabaseName}");
database = client.GetDatabase(DatabaseName);
Console.WriteLine($"{Environment.NewLine} database { database.Id.ToString()}");

// Read all databases
string findQueryText = "SELECT * FROM c";
using (FeedIterator<DatabaseProperties> feedIterator = client.GetDatabaseQueryIterator<DatabaseProperties>(findQueryText))
{
    while (feedIterator.HasMoreResults)
    {
        FeedResponse<DatabaseProperties> databaseResponses = await feedIterator.ReadNextAsync();
        foreach (DatabaseProperties _database in databaseResponses)
        {
            Console.WriteLine($"{ Environment.NewLine} database {_database.Id.ToString()}");
        }
    }
}

Excluir um banco de dados

// Delete a database
await client.GetDatabase(DatabaseName).DeleteAsync();
Console.WriteLine($"{ Environment.NewLine} database {DatabaseName} deleted.");

// Delete all databases in an account
string deleteQueryText = "SELECT * FROM c";
using (FeedIterator<DatabaseProperties> feedIterator = client.GetDatabaseQueryIterator<DatabaseProperties>(deleteQueryText))
{
    while (feedIterator.HasMoreResults)
    {
        FeedResponse<DatabaseProperties> databaseResponses = await feedIterator.ReadNextAsync();
        foreach (DatabaseProperties _database in databaseResponses)
        {
            await client.GetDatabase(_database.Id).DeleteAsync();
            Console.WriteLine($"{ Environment.NewLine} database {_database.Id} deleted");
        }
    }
}

Operações de contêiner

Criar um contêiner (dimensionamento automático + vida útil com expiração)

private static async Task CreateManualThroughputContainer(Database database)
{
    // Set throughput to the minimum value of 400 RU/s manually configured throughput
    string containerIdManual = ContainerName + "_Manual";
    ContainerResponse container = await database.CreateContainerIfNotExistsAsync(
        id: containerIdManual,
        partitionKeyPath: partitionKeyPath,
        throughput: 400);
}

// Create container with autoscale
private static async Task CreateAutoscaleThroughputContainer(Database database)
{
    string autoscaleContainerId = ContainerName + "_Autoscale";
    ContainerProperties containerProperties = new ContainerProperties(autoscaleContainerId, partitionKeyPath);

    Container container = await database.CreateContainerIfNotExistsAsync(
        containerProperties: containerProperties,
        throughputProperties: ThroughputProperties.CreateAutoscaleThroughput(autoscaleMaxThroughput: 4000);
}

// Create a container with TTL Expiration
private static async Task CreateContainerWithTtlExpiration(Database database)
{
    string containerIdManualwithTTL = ContainerName + "_ManualTTL";

    ContainerProperties properties = new ContainerProperties
        (id: containerIdManualwithTTL,
        partitionKeyPath: partitionKeyPath);

    properties.DefaultTimeToLive = (int)TimeSpan.FromDays(1).TotalSeconds; //expire in 1 day

    ContainerResponse containerResponse = await database.CreateContainerIfNotExistsAsync(containerProperties: properties);
    ContainerProperties returnedProperties = containerResponse;
}

Ler propriedades de contêiner

private static async Task ReadContainerProperties(Database database)
{
    string containerIdManual = ContainerName + "_Manual";
    Container container = database.GetContainer(containerIdManual);
    ContainerProperties containerProperties = await container.ReadContainerAsync();
}

Excluir um contêiner

private static async Task DeleteContainers(Database database)
{
    string containerIdManual = ContainerName + "_Manual";

    // Delete a container
    await database.GetContainer(containerIdManual).DeleteContainerAsync();

    // Delete all CosmosContainer resources for a database
    using (FeedIterator<ContainerProperties> feedIterator = database.GetContainerQueryIterator<ContainerProperties>())
    {
        while (feedIterator.HasMoreResults)
        {
            foreach (ContainerProperties _container in await feedIterator.ReadNextAsync())
            {
                await database.GetContainer(_container.Id).DeleteContainerAsync();
                Console.WriteLine($"{Environment.NewLine}  deleted container {_container.Id}");
            }
        }
    }
}

Operações de item e de consulta

Criar um item

private static async Task CreateItemAsync(Container container)
{
    // Create a SalesOrder POCO object
    SalesOrder salesOrder1 = GetSalesOrderSample("Account1", "SalesOrder1");
    ItemResponse<SalesOrder> response = await container.CreateItemAsync(salesOrder1,
        new PartitionKey(salesOrder1.AccountNumber));
}

private static async Task RunBasicOperationsOnDynamicObjects(Container container)
{
    // Dynamic Object
    dynamic salesOrder = new
    {
        id = "SalesOrder5",
        AccountNumber = "Account1",
        PurchaseOrderNumber = "PO18009186470",
        OrderDate = DateTime.UtcNow,
        Total = 5.95,
    };
    Console.WriteLine("\nCreating item");
    ItemResponse<dynamic> response = await container.CreateItemAsync<dynamic>(
        salesOrder, new PartitionKey(salesOrder.AccountNumber));
    dynamic createdSalesOrder = response.Resource;
}

Ler todos os itens em um contêiner

private static async Task ReadAllItems(Container container)
{
    // Read all items in a container
    List<SalesOrder> allSalesForAccount1 = new List<SalesOrder>();

    using (FeedIterator<SalesOrder> resultSet = container.GetItemQueryIterator<SalesOrder>(
        queryDefinition: null,
        requestOptions: new QueryRequestOptions()
        {
            PartitionKey = new PartitionKey("Account1"),
            MaxItemCount = 5
        }))
    {
        while (resultSet.HasMoreResults)
        {
            FeedResponse<SalesOrder> response = await resultSet.ReadNextAsync();
            SalesOrder salesOrder = response.First();
            Console.WriteLine($"\n1.3.1 Account Number: {salesOrder.AccountNumber}; Id: {salesOrder.Id}");
            allSalesForAccount1.AddRange(response);
        }
    }
}

Itens de consulta

Alterações no SqlQuerySpec (QueryDefinition no SDK v3.0)

A classe SqlQuerySpec no SDK v2 agora foi renomeada para QueryDefinition no SDK v3.

SqlParameterCollection e SqlParameter foram removidos. Agora, os parâmetros são adicionados ao QueryDefinition com um modelo de construtor usando QueryDefinition.WithParameter. Os usuários podem acessar os parâmetros com QueryDefinition.GetQueryParameters

private static async Task QueryItems(Container container)
{
    // Query for items by a property other than Id
    QueryDefinition queryDefinition = new QueryDefinition(
        "select * from sales s where s.AccountNumber = @AccountInput")
        .WithParameter("@AccountInput", "Account1");

    List<SalesOrder> allSalesForAccount1 = new List<SalesOrder>();
    using (FeedIterator<SalesOrder> resultSet = container.GetItemQueryIterator<SalesOrder>(
        queryDefinition,
        requestOptions: new QueryRequestOptions()
        {
            PartitionKey = new PartitionKey("Account1"),
            MaxItemCount = 1
        }))
    {
        while (resultSet.HasMoreResults)
        {
            FeedResponse<SalesOrder> response = await resultSet.ReadNextAsync();
            SalesOrder sale = response.First();
            Console.WriteLine($"\n Account Number: {sale.AccountNumber}; Id: {sale.Id};");
            allSalesForAccount1.AddRange(response);
        }
    }
}

Excluir um item

private static async Task DeleteItemAsync(Container container)
{
    ItemResponse<SalesOrder> response = await container.DeleteItemAsync<SalesOrder>(
        partitionKey: new PartitionKey("Account1"), id: "SalesOrder3");
}

Consulta do feed de alterações

private static async Task QueryChangeFeedAsync(Container container)
{
    FeedIterator<SalesOrder> iterator = container.GetChangeFeedIterator<SalesOrder>(ChangeFeedStartFrom.Beginning(), ChangeFeedMode.Incremental);

    string continuation = null;
    while (iterator.HasMoreResults)
    {
        FeedResponse<SalesOrder> response = await iteratorForTheEntireContainer.ReadNextAsync();
    
        if (response.StatusCode == HttpStatusCode.NotModified)
        {
            // No new changes
            continuation = response.ContinuationToken;
            break;
        }
        else 
        {
            // Process the documents in response
        }
    }
}

Próximas etapas