Process change feed events using the Azure Cosmos DB for NoSQL SDK

The Azure Cosmos DB for NoSQL change feed is the key to creating supplemental applications driven by events from the platform. The .NET SDK for the Azure Cosmos DB for NoSQL ships with a suite of classes to build your applications that integrate with the change feed and listen for notifications about operations within your containers.

In this lab, you will use the change feed processor functionality in the .NET SDK to create an application that is notified with a create or update operation is performed on an item in the specified container.

Prepare your development environment

If you have not already cloned the lab code repository for DP-420 to the environment where you’re working on this lab, follow these steps to do so. Otherwise, open the previously cloned folder in Visual Studio Code.

  1. Start Visual Studio Code.

    📝 If you are not already familiar with the Visual Studio Code interface, review the Get Started guide for Visual Studio Code

  2. Open the command palette and run Git: Clone to clone the https://github.com/microsoftlearning/dp-420-cosmos-db-dev GitHub repository in a local folder of your choice.

    💡 You can use the CTRL+SHIFT+P keyboard shortcut to open the command palette.

  3. Once the repository has been cloned, open the local folder you selected in Visual Studio Code.

Create an Azure Cosmos DB for NoSQL account

Azure Cosmos DB is a cloud-based NoSQL database service that supports multiple APIs. When provisioning an Azure Cosmos DB account for the first time, you will select which of the APIs you want the account to support (for example, Mongo API or NoSQL API). Once the Azure Cosmos DB for NoSQL account is done provisioning, you can retrieve the endpoint and key and use them to connect to the Azure Cosmos DB for NoSQL account using the Azure SDK for .NET or any other SDK of your choice.

  1. In a new web browser window or tab, navigate to the Azure portal (portal.azure.com).

  2. Sign into the portal using the Microsoft credentials associated with your subscription.

  3. Select + Create a resource, search for Cosmos DB, and then create a new Azure Cosmos DB for NoSQL account resource with the following settings, leaving all remaining settings to their default values:

    Setting Value
    Subscription Your existing Azure subscription
    Resource group Select an existing or create a new resource group
    Account Name Enter a globally unique name
    Location Choose any available region
    Capacity mode Serverless

    📝 Your lab environments may have restrictions preventing you from creating a new resource group. If that is the case, use the existing pre-created resource group.

  4. Wait for the deployment task to complete before continuing with this task.

  5. Go to the newly created Azure Cosmos DB account resource and navigate to the Keys pane.

  6. This pane contains the connection details and credentials necessary to connect to the account from the SDK. Specifically:

    1. Notice the URI field. You will use this endpoint value later in this exercise.

    2. Notice the PRIMARY KEY field. You will use this key value later in this exercise.

  7. Select Data Explorer from the resource menu.

  8. In the Data Explorer pane, expand New Container and then select New Database.

  9. In the New Database popup, enter the following values for each setting, and then select OK:

    Setting Value
    Database id cosmicworks
  10. Back in the Data Explorer pane, observe the cosmicworks database node within the hierarchy.

  11. In the Data Explorer pane, select New Container.

  12. In the New Container popup, enter the following values for each setting, and then select OK:

    Setting Value
    Database id Use existing | cosmicworks
    Container id products
    Partition key /categoryId
  13. Back in the Data Explorer pane, expand the cosmicworks database node and then observe the products container node within the hierarchy.

  14. In the Data Explorer pane, select New Container again.

  15. In the New Container popup, enter the following values for each setting, and then select OK:

    Setting Value
    Database id Use existing | cosmicworks
    Container id productslease
    Partition key /partitionKey
  16. Back in the Data Explorer pane, expand the cosmicworks database node and then observe the productslease container node within the hierarchy.

  17. Return to Visual Studio Code.

Implement the change feed processor in the .NET SDK

The Microsoft.Azure.Cosmos.Container class ships with a series of methods to build the change feed processor fluently. To start, you need a reference to your monitored container, your lease container, and a delegate in C# (to handle each batch of changes) to get started.

  1. In the Explorer pane, browse to the 13-change-feed folder.

  2. Open the product.cs code file.

  3. Observe the Product class and its corresponding properties. Specifically, this lab will use the id and name properties.

  4. Back in the Explorer pane of Visual Studio Code, open the script.cs code file.

  5. Update the existing variable named endpoint with its value set to the endpoint of the Azure Cosmos DB account you created earlier.

     string endpoint = "<cosmos-endpoint>";
    

    📝 For example, if your endpoint is: https­://dp420.documents.azure.com:443/, then the C# statement would be: string endpoint = “https­://dp420.documents.azure.com:443/”;.

  6. Update the existing variable named key with its value set to the key of the Azure Cosmos DB account you created earlier.

     string key = "<cosmos-key>";
    

    📝 For example, if your key is: fDR2ci9QgkdkvERTQ==, then the C# statement would be: string key = “fDR2ci9QgkdkvERTQ==”;.

  7. Use the GetContainer method of the client variable to retrieve the existing container using the name of the database (cosmicworks) and the name of the container (products) and store the result in a variable named sourceContainer of type Container:

     Container sourceContainer = client.GetContainer("cosmicworks", "products");
    
  8. Use the GetContainer method of the client variable to retrieve the existing container using the name of the database (cosmicworks) and the name of the container (productslease) and store the result in a variable named leaseContainer of type Container:

     Container leaseContainer = client.GetContainer("cosmicworks", "productslease");
    
  9. Create a new delegate variable named handleChanges of type ChangesHandler<> using an empty asynchronous anonymous function that has two input parameters:

    1. A parameter named changes of type IReadOnlyCollection<Product>.

    2. A parameter named cancellationToken of type CancellationToken.

     ChangesHandler<Product> handleChanges = async (
         IReadOnlyCollection<Product> changes,
         CancellationToken cancellationToken
     ) => {
     };
    
  10. Within the anonymous function, use the built-in Console.WriteLine static method to print the raw string START\tHandling batch of changes…:

     Console.WriteLine($"START\tHandling batch of changes...");
    
  11. Still within the anonymous function, create a foreach loop that iterates over the changes variable using the variable product to represent an instance of type Product:

     foreach(Product product in changes)
     {
     }
    
  12. Within the foreach loop of the anonymous function, use the built-in asynchronous Console.WriteLineAsync static method to print the id and name properties of the product variable:

     await Console.Out.WriteLineAsync($"Detected Operation:\t[{product.id}]\t{product.name}");
    
  13. Outside of the foreach loop and anonymous function, create a new variable named builder that stores the result of invoking GetChangeFeedProcessorBuilder<> on the sourceContainer variable using the following parameters:

    Parameter Value
    processorName productsProcessor
    onChangesDelegate handleChanges
     var builder = sourceContainer.GetChangeFeedProcessorBuilder<Product>(
         processorName: "productsProcessor",
         onChangesDelegate: handleChanges
     );
    
  14. Invoke the WithInstanceName method with a parameter of consoleApp, the WithLeaseContainer method with a parameter of leaseContainer, and the Build method fluently on the builder variable storing the result in a variable named processor of type ChangeFeedProcessor:

     ChangeFeedProcessor processor = builder
         .WithInstanceName("consoleApp")
         .WithLeaseContainer(leaseContainer)
         .Build();
    
  15. Asynchronously invoke the StartAsync of the processor variable:

     await processor.StartAsync();
    
  16. Use built-in Console.WriteLine and Console.ReadKey static methods to print output to the console and to have the application wait for a key press:

     Console.WriteLine($"RUN\tListening for changes...");
     Console.WriteLine("Press any key to stop");
     Console.ReadKey();  
    
  17. Asynchronously invoke the StopAsync of the processor variable:

     await processor.StopAsync();
    
  18. Once you are done, your code file should now include:

     using Microsoft.Azure.Cosmos;
     using static Microsoft.Azure.Cosmos.Container;
    
     string endpoint = "<cosmos-endpoint>";
     string key = "<cosmos-key>";
    
     CosmosClient client = new CosmosClient(endpoint, key);
        
     Container sourceContainer = client.GetContainer("cosmicworks", "products");
     Container leaseContainer = client.GetContainer("cosmicworks", "productslease");
        
     ChangesHandler<Product> handleChanges = async (
         IReadOnlyCollection<Product> changes,
         CancellationToken cancellationToken
     ) => {
         Console.WriteLine($"START\tHandling batch of changes...");
         foreach(Product product in changes)
         {
             await Console.Out.WriteLineAsync($"Detected Operation:\t[{product.id}]\t{product.name}");
         }
     };
        
     var builder = sourceContainer.GetChangeFeedProcessorBuilder<Product>(
             processorName: "productsProcessor",
             onChangesDelegate: handleChanges
         );
        
     ChangeFeedProcessor processor = builder
         .WithInstanceName("consoleApp")
         .WithLeaseContainer(leaseContainer)
         .Build();
        
     await processor.StartAsync();
        
     Console.WriteLine($"RUN\tListening for changes...");
     Console.WriteLine("Press any key to stop");
     Console.ReadKey();
        
     await processor.StopAsync();
    
  19. Save the script.cs file.

  20. In Visual Studio Code, open the context menu for the 13-change-feed folder and then select Open in Integrated Terminal to open a new terminal instance.

  21. Build and run the project using the dotnet run command:

     dotnet run
    
  22. Leave both Visual Studio Code and the terminal open.

    📝 You will use another tool to generate items in your Azure Cosmos DB for NoSQL container. Once you generate the items, you will return to this terminal to observe the output. Do not close the terminal prematurely.

Seed your Azure Cosmos DB for NoSQL account with sample data

You will use a command-line utility that creates a cosmicworks database and a products container. The tool will then create a set of items that you will observe using the change feed processor running in your terminal window.

  1. In Visual Studio Code, open the Terminal menu and then select Split Terminal to open a new terminal side by side with your existing instance.

  2. Install the cosmicworks command-line tool for global use on your machine.

     dotnet tool install cosmicworks --global --version 1.*
    

    💡 This command may take a couple of minutes to complete. This command will output the warning message (*Tool ‘cosmicworks’ is already installed’) if you have already installed the latest version of this tool in the past.

  3. Run cosmicworks to seed your Azure Cosmos DB account with the following command-line options:

    Option Value
    –endpoint The endpoint value you copied earlier in this lab
    –key The key value you coped earlier in this lab
    –datasets product
     cosmicworks --endpoint <cosmos-endpoint> --key <cosmos-key> --datasets product
    

    📝 For example, if your endpoint is: https­://dp420.documents.azure.com:443/ and your key is: fDR2ci9QgkdkvERTQ==, then the command would be: cosmicworks --endpoint https://dp420.documents.azure.com:443/ --key fDR2ci9QgkdkvERTQ== --datasets product

  4. Wait for the cosmicworks command to finish populating the account with a database, container, and items.

  5. Observe the terminal output from your .NET application. The terminal outputs a Detected Operation message for each change that was sent to it using the change feed.

  6. Close both integrated terminals.

  7. Close Visual Studio Code.