Cosmos DB SDK v3 preview

Akos Nagy
Apr 17, 2019

A while back I reviewed the EF Core Cosmos DB provider preview. But that's not the only big change coming to Cosmos DB. The actual SDK that allows you access to all the features of the service is also getting an update. While I think it's still a couple of relelases away from RTM, I thought it might be worth looking into it.

The new programming model

Let's be honest: the v2 SDK was a bit much to handle with the self-links and the uri factories and all the other boilerplate. With the new SDK, this is all gone. So first, I installed the new Microsoft.Azure.Cosmos NuGet package, then set up a Nrothwind sample database based on my awesome Northwind project. And then, get cracking.

Just the basic 'read all' operation looks so much nicer with the new SDK:

using (var cosmosClient = new CosmosClient("https://localhost:8081", "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw=="))
{
  var container = cosmosClient.Databases["NorthwindDB"].Containers["Northwind"];
  var query = container.Items.CreateItemQuery<Shipper>("select * from Northwind n", "Shipper");
  while (query.HasMoreResults)
  {
    var data = await query.FetchNextSetAsync();
    foreach (var shipper in data)
    {
      Console.WriteLine(shipper.CompanyName);
    }
}

Just create a client with the usual authentication info, then use simple indexers to get the database reference and then the container reference. Nice. And then, just create a query by specifying the query string and the partition key. When you're done, you have to use a database-cursor-like mechanism to get all the results (I do see this as an opportunity to use the async iterator feature of C# 8).

But we also get new features: the results can also be read directly into a stream, and then you can handle the deserialization yourself. With this method, you also get access to some of the query metadata, like the RU charge of the status code:

var stream = container.Items.CreateItemQueryAsStream("select * from Northwind n", 4, "Shipper");
while (stream.HasMoreResults)
{
  var data = await stream.FetchNextSetAsync();
  Console.WriteLine(data.RequestCharge);
  Console.WriteLine(data.StatusCode);
  if (data.IsSuccess)
  {
    using (var streamReader = new StreamReader(data.Content))
    {
      Console.WriteLine(await streamReader.ReadToEndAsync());
    }
  }
}

Creating a database or a container is just as simple, you just have to use the collection objects and then call methods like CreateContainerIfNotExistsAsync(). It's very intuitive, easy to read. Nice job.

Handlers

As a way of hooking into the query execution pipeline, you can now create handlers: classes that have a specific interface that can be used during operation execution. For example, you can create one that logs everything to the console:

public class LoggingHandler : CosmosRequestHandler
{
  public override async Task<CosmosResponseMessage> SendAsync(CosmosRequestMessage request, CancellationToken cancellationToken)
  {
    Console.WriteLine($"{request.Method.Method} - {request.RequestUri.ToString()}");
    CosmosResponseMessage response = await base.SendAsync(request, cancellationToken);
    Console.WriteLine("Operation done");
    Console.WriteLine((int)response.StatusCode);
    return response;
  }
}

And then, you can add it to the client if you create it via a builder:

var builder = new CosmosClientBuilder("AccountEndpoint=https://localhost:8081/;AccountKey=C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==");
builder.AddCustomHandlers(new LoggingHandler());
using (var cosmosClient = builder.Build())
{
  var container = cosmosClient.Databases["NorthwindDB"].Containers["Northwind"];
  await container.Items.DeleteItemAsync<Shipper>("Shipper", "cf372ac5-f937-4f28-8171-897159f7b30d");
}

And now every operation is logged to the console like this:

With this method you can implement retries, error handling or concurrency handling with a clean, DRY method.

What's next

This preview is very preview: no LInQ support, no change feed handling and a couple of other missing features. But I do like how the SDK is looking and how they simplified the programming model — can't wait for a stable release so I can rewrite my LinqPad Cosmos DB driver.

Akos Nagy
Posted in