the cloud way

Cosmos .NET SDK v3 upgrade guide and tips

Published Reading time
image

 

Introduction

A change in branding for the Azure globally distributed, multi-model database service from DocumentDB to CosmosDB was admittedly, not understood by many initially. Fast forward to today, and the renaming makes sense in that the 'DocumentDB' or SQL api is only one part of the database system that comes in several flavors of apis you can use with Cosmos: SQL API (an artist formerly known as DocumentDB), MongoDB, Table, Gremlin or Cassandra. In this post we will look specifically the SQL API .NET SDK V3 change highlights.

1.) Name Changes

//Old
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="2.2.0" />
//

//New v3
<PackageReference Include="Microsoft.Azure.Cosmos" Version="3.4.1" />
//Old
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;
//

//New v3
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Cosmos.Linq;
using Microsoft.Azure.Cosmos.Scripts;
using Microsoft.Azure.Cosmos.Fluent;
Package Microsoft.Azure.DocumentDB.Core Microsoft.Azure.Cosmos
Namespace Microsoft.Azure.Documents.Client Removed
Namespace Microsoft.Azure.Documents Microsoft.Azure.Cosmos
Namespace Microsoft.Azure.Documents.Linq Microsoft.Azure.Cosmos.Linq
Namespace None Microsoft.Azure.Cosmos.Scripts
Namespace None Microsoft.Azure.Cosmos.Fluent
Class DocumentClient CosmosClient
Class ConnectionPolicy CosmosClientOptions
Class DocumentCollection Container
Class RequestOptions ItemRequestOptions
Class FeedRequestOptions QueryRequestOptions
Class SqlQuerySpec QueryDefinition
Class StoredProcedure Removed
Class StoredProcedureResponse<T> StoredProcedureExecuteResponse<T>

2.) Fluent Apis

A blog post regarding the overall api design goes in more depth. Below is an example taken from the post demonstrating the fluent api in action creating a CosmosClient and a container with indexing policies.

3.) Client Changes

The Cosmos SDK team made some great decisions here to really decompose the new CosmosClient. The old DocumentClient seemed to be used for almost all activites. Now, the respective operations hang off of the dependent objects as opposed to needing the client instance to manage documents (now items) or stored procedures. Let's look at some examples.

New Client with CosmosClientOptions

Be sure to set the CosmosClientOptions.SerializerOptions.PropertyNamingPolicy if you have a preference of camelCasing the documents server side. The Linq query processor now supports this setting when generating queries from the SDK.

//Old DocumentClient
    var policy = new ConnectionPolicy()
                {	
                     ConnectionMode = ConnectionMode.Gateway,
                     ConnectionProtocol = Protocol.Https
                };
    var oldClient = new DocumentClient(new Uri("serviceUri"), 
                    "secretAuthKey", 
                    policy, 
                    ConsistencyLevel.Session);
//

//New v3 CosmosClient
    var options = new CosmosClientOptions()
    {	                
            ConnectionMode = ConnectionMode.Gateway,	
            ConsistencyLevel = ConsistencyLevel.Session,
            SerializerOptions = new CosmosSerializationOptions()
    };
    options.SerializerOptions.PropertyNamingPolicy 
                        = CosmosPropertyNamingPolicy.CamelCase;
    // Controls the server side naming, very important for how you author queries.
    // Linq queries will now respect this setting. The old SDK did not.

    var newClient = new CosmosClient("serviceUri", "secretAuthKey", options);
//

4.) DocumentCollection vs Container

The SelfLink requirement also takes a dirt nap in the new v3 SDK. Thanks SDK team for putting the SelfLink parameter out of it's misery!

Let's have a moment of silence for the DocumentCollection and the optional partition key. The partition key is now required on the Container and if you didn't have a partition key defined before, it might be worth designing a partition key scheme and migrating data to a new Container.

The Container replaces the DocumentCollection and is a much more functional object with operations that used to be handled by the client. Of course, you could use the fluent api as shown above. However, below shows more of an apples to apples comparsion of the api operations to the v2 SDK.

New Container

//Old
    var dbResponse = oldClient
            .CreateDatabaseIfNotExistsAsync(
                new Database { Id = "myDbName" });
    Database db = dbResponse.Resource;

// You are on borrowed time, SelfLink...
    var docCollectionResponse = oldClient
            .CreateDocumentCollectionIfNotExistsAsync(
                db.SelfLink, "myContainer", new RequestOptions());
    DocumentCollection myContainer = docCollectionResponse.Resource;
//

//New v3
    Database db = await newClient
                    .CreateDatabaseIfNotExistsAsync("myDbName");
    
// Look mom, no selfLink needed!
// But you do need that partition key.
// The partition key path here must be defined on any object in the container. 
// e.g. myDocument.partitionKey 
    var containerResponse = await db.CreateContainerIfNotExistsAsync(
                new ContainerProperties("myContainer", "/partitionKey"));
    Container myContainer = containerResponse.Container;
//

5.) Document vs Item

Document operations have been renamed to Item. The Item operations now hang off the Container object instead of the Client.

Create Document/Item

//Setup
    // Where DocumentClass is an object you would like to store
    DocumentClass docClass = new DocumentClass();

//Old
    // Notice the ref to the oldClient needed to create a document .
    // And the Documents SelfLink.
    Document doc = await oldClient.CreateDocumentAsync(
                        myContainer.DocumentsLink, 
                        docClass,	            
                        new RequestOptions(), true);
//

//New v3
    // No client object needed, just need the Container
    // The new ItemRequestOptions class is optional
    // Make sure to set the partition key on the object
    docClass.PartitionKey = "somePartitionKey";
    DocumentClass doc = await myContainer.CreateItemAsync<DocumentClass>(
                docClass, 
                new PartitionKey(docClass.PartitionKey), 
                new ItemRequestOptions());
//

Query Documents/Items

Notice the class name change from SqlQuerySpec to QueryDefinition and the clean fluent api design .WithParameter(). Also, camelCasing properties did not work correctly with the old SDK when using Linq queries. That is now fixed in v3. Continuation tokens are now more accessable in the api for large resultsets via the FeedIterator<T>

Using SQL With Parameters
//Setup
    // Where DocumentClass is an object you would like to query
    string partitionKey = "somePartitionKey";
    List<DocumentClass> results = new List<DocumentClass>();

//Old
    SqlQuerySpec sqlQuery = new SqlQuerySpec("SELECT VALUE r " +
                            "FROM ROOT r " +
                            "WHERE (r.partitionKey = @partitionKey) ", 
                            new SqlParameterCollection(){
                            new SqlParameter("@partitionKey", partitionKey)
                        });
    //No return of the continuationToken here
    IQueryable<dynamic> docQuery = oldClient.CreateDocumentQuery(myContainer.DocumentsLink, 
                    sqlQuery,
                    new FeedOptions());

    //.ToList() is needed to execute the query
    foreach (dynamic doc in docQuery.ToList())
    {
        DocumentClass dc = doc;
        results.Add(dc);
    }    
//

//New v3
    // Notice the fluent .WithParameter goodness here
    QueryDefinition sqlQuery = new QueryDefinition("SELECT VALUE r " +
                            "FROM ROOT r " +
                            "WHERE (r.partitionKey = @partitionKey) ")
                            .WithParameter("@partitionKey", partitionKey);
            
    string continuationToken = null;
    do
    {
        FeedIterator<DocumentClass> feedIterator = 
            myContainer.GetItemQueryIterator<DocumentClass>(
                    sqlQuery, 
                    continuationToken: continuationToken);

        while (feedIterator.HasMoreResults)
        {
            FeedResponse<DocumentClass> feedResponse = await feedIterator.ReadNextAsync();
            continuationToken = feedResponse.ContinuationToken;
            foreach (DocumentClass item in feedResponse)
            {
                results.Add(item);
            }
        }
    } while (continuationToken != null);
//
Using Linq
//Setup
    // Where DocumentClass is an object you would like to query
//Old
    IOrderedQueryable<DocumentClass> docQueryable = 
            oldClient.CreateDocumentQuery<DocumentClass>(myContainer.DocumentsLink);     
    var myResults = docQueryable.ToList();       
//

//New v3
    IOrderedQueryable<DocumentClass> docQueryable = 
            myContainer.GetItemLinqQueryable<DocumentClass>(true);
    var myResults = docQueryable.ToList();
//

6.) Stored Procedures

The StoredProcedure object has been traded in for a simple string stored procedure id that is referenced to execute a sproc. Also, a partition key is now required to execute the stored procedure in addition to the stored procedure id.

//Stored Proc js
function getByIdSproc_V1(id) {
    var collection = getContext().getCollection();
    //Query document by Id 
    var isAccepted = collection.queryDocuments(
        collection.getSelfLink(),
        'SELECT * FROM root r WHERE r.id = "' + id + '"',
        function (err, feed, options) {
            if (err) throw err;
            if (!feed || !feed.length) {
                getContext().getResponse().setBody("");
            }
            else {
                getContext().getResponse().setBody(feed[0]);
            }
        });

}
//Setup
    string strSprocId = "getByIdSproc_V1";
    string body = *"from the js above"*
//Old
    //Create the sproc
    StoredProcedure getByIdSproc = 
            await oldClient.CreateStoredProcedureAsync(myContainer.SelfLink,
                        new StoredProcedure()
                        {	                     
                            Id = strSprocId,	      
                            Body = body,	
                        });
    //Execute the sproc
    string id = "someIdToLookup";
    StoredProcedureResponse<DocumentClass> response = 
            await oldClient.ExecuteStoredProcedureAsync<DocumentClass>(getByIdSproc.SelfLink,
                procedureParams: new dynamic[] { id });
    DocumentClass result = response.Resource;
//

//New v3

    //Execute the sproc
    string id = "someIdToLookup";
    string partitionKey = "somePartitionKeyToLookup";

    StoredProcedureExecuteResponse<DocumentClass> response 
            = await myContainer.Scripts.ExecuteStoredProcedureAsync<TUser>(
                    strSprocId,
                    new PartitionKey(partitionKey),
                    new dynamic[] { id });

            return response.Resource;
//

Summary

This article was intended to give the highlights of upgrading your project from DocumentDB .NET SDK v2 to Cosmos .NET SDK v3. First, we reviewed naming conventions and fluent api design. Also, we looked at some common examples between the old v2 code and new v3 code to expedite your upgrade. Finally, we pointed out a few breaking changes and fixes like:

  • Namespace and Class mapping
  • Partition Key required on creating new Containers and executing Stored Procedures
  • Camel Casing properties from Linq queries are generated correctly
  • Where the commonly used classes and operations live now

Open a discussion

Open Source Projects

 
image
Project Title

Project intro lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes.

NuGet Badge   NuGet Badge

image
Project Title

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

NuGet Badge   NuGet Badge

image
Project Title

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

NuGet Badge

image
Project Title

Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus.

NuGet Badge