C# Azure Functions to access CosmosDB

Interacting with CosmosDB from a serverless application can be achieved with very few lines of code using C#.

Pre-requisites:

  • CosmosDB resource
  • An Azure Functions resource
  • An input binding (the CosmosDB resource) is required for read operations, while an output binding (the CosmosDB resource) is needed for the write operations. For each operation, I share the specific function.json required.

Considerations:

  • In each code there is a model (class) of your data. In our example, we are using a Student class.
  • Each operation is an HTTP trigger.

Let’s go!

Operation 1. Retrieving data:
function.json is available here.

using System.Net;
using Microsoft.AspNetCore.Mvc;

public static async Task<IActionResult> Run(HttpRequest req, IEnumerable<Student> inputDocument, ILogger log)
{
  var students = (List<Student>) inputDocument;
  return new OkObjectResult(students);
}

Comments:
No “special” library/SDK is required to read from CosmosDB since this is handled by the binding in function.json!

Testing:
Simply send a GET request to the URL provided by Azure Functions and you’ll get your data:

Retrieving data

Operation 2. Adding data:
function.json is available here

#r "Newtonsoft.Json"

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

public static IActionResult Run(HttpRequest req, out object outputDocument, ILogger log)
{
  var body = new StreamReader(req.Body).ReadToEnd();
  dynamic data = JsonConvert.DeserializeObject(body);

  outputDocument = new
  {
    id = data.id,
    faculty = data.faculty,
    Name = data.Name,
    Semester = data.Semester
  };

  return (ActionResult)new OkResult();
}

Comments:
The return statement writes new data in CosmosDB since we set that resource as an output binding (see the associated function.json).

Testing:
Send a POST request to the URL provided by Azure Functions including the new data (Student object) in the Body.

Result is 200 OK =)

Result of adding data

Operation 3. Updating data:
function.json is available here

#r "Newtonsoft.Json"
#r "Microsoft.Azure.DocumentDB.Core"
#r "Microsoft.Azure.WebJobs.Extensions.CosmosDB"

using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using Microsoft.Azure.Documents.Client;

public static async Task<IActionResult> Run(
  HttpRequest req, 
  [CosmosDB(ConnectionStringSetting = "universitylb7_DOCUMENTDB")] DocumentClient outputDocument, 
  ILogger log, string id, string faculty)
{
  string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
  var updated = JsonConvert.DeserializeObject<Student>(requestBody);

  var option = new FeedOptions { EnableCrossPartitionQuery = true };
  var collectionUri = UriFactory.CreateDocumentCollectionUri("studentsdb", "students");

  var document = outputDocument.CreateDocumentQuery(collectionUri, option).Where(t => t.Id == id)
        .AsEnumerable().FirstOrDefault();

  if (document == null)
  {
    return new NotFoundResult();
  }

  document.SetPropertyValue("Name", updated.Name);
  document.SetPropertyValue("Semester", updated.Semester);

  await outputDocument.ReplaceDocumentAsync(document);

  return new OkResult();
}

Comments:
To update (and delete) we need the libraries ( Microsoft.Azure.DocumentDB.Core and Microsoft.Azure.WebJobs.Extensions.CosmosDB) since we need to search and then replace the document with the latest content.

Moreover, in the function.json you will see that a route is specified: it means that the URL must include the id and partition key of the document we want to update (the combination of both elements must be unique in a collection). And of course, you will include the new data in the Body request.

Once again, the result is a nice 200 OK.

200 OK

Operation 4. Deleting data:
function.json is available here

#r "Newtonsoft.Json"
#r "Microsoft.Azure.DocumentDB.Core"
#r "Microsoft.Azure.WebJobs.Extensions.CosmosDB"

using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.Documents.Client;

public static async Task<IActionResult> Run(
  HttpRequest req, 
  [CosmosDB(ConnectionStringSetting = "universitylb7_DOCUMENTDB")] DocumentClient outputDocument, 
  ILogger log, string id, string faculty)
{
    var option = new FeedOptions { EnableCrossPartitionQuery = true };
    var collectionUri = UriFactory.CreateDocumentCollectionUri("studentsdb", "students");

    var document = outputDocument.CreateDocumentQuery(collectionUri, option).Where(t => t.Id == id).AsEnumerable().FirstOrDefault();

    if (document == null)
    {
        return new NotFoundResult();
    }

    await outputDocument.DeleteDocumentAsync(document.SelfLink, new RequestOptions
    {
    PartitionKey = new Microsoft.Azure.Documents.PartitionKey(faculty)
    });

    return new OkResult();
}

Comments:
The code to delete is quite similar to the update one, except we don’t replace here (but delete) the document.

Testing:
Just include the id and faculty in the URL query parameters.

Bye-bye data!


And with that, we have set a serverless API that can interact with CosmosDB.

I hope that this blog post was interesting and useful for you. I invite you to visit my blog for more technical posts about Xamarin, Azure, and the .NET ecosystem. I write in Spanish language =)

Thanks for your time, and enjoy the rest of the C# Advent Calendar publications!

See you next time,
Luis

This blog featured as part of Azure Week. Find more great Azure content here.

About the Author:

Mexican Microsoft #MVP in AI and Dev Tech | PhD student @UniverzitaTBatiXamarin Dev #Lince@ItcelayaOficial | Writing my own story #FlyHigh

Reference:

Beltran, L. (2021). C# Azure Functions to access CosmosDB. Available at: https://dev.to/icebeam7/c-azure-functions-to-access-cosmosdb-5632 [Accessed: 30th June 2022].

Share this on...

Rate this Post:

Share: