Channels ▼
RSS

Azure DocumentDB: Working with Microsoft's NoSQL Database in the Cloud


Microsoft Azure DocumentDB is a new NoSQL document database service added to Microsoft Azure that supports JSON and JavaScript inside the database engine. DocumentDB supports SQL syntax to query JSON documents, and so enables you to leverage existing SQL knowledge while working with a NoSQL database. In this article, I provide an example of coding for this new Azure service with C#, DocumentDB SQL, and LINQ.

Working with Databases, Collections, and Documents

DocumentDB is a fully managed and schema-free JSON document database service that uses JavaScript and an HTTP REST API. It is available only as an Azure service, which provides elastic scale for both throughput and storage. There is no DocumentDB installer and you cannot work with this database on your own dedicated hardware. At the time of writing, Microsoft offers a free one month trial for new Azure accounts. In my examples below, I assume that you already have an Azure account and you are ready to work with the DocumentDB service.

DocumentDB allows you to use server-side JavaScript to add user-defined functions in your SQL queries. You can also create stored procedures and triggers in JavaScript. An SDK enables you to work with the following languages and platforms:

  • .NET, including support for LINQ (Language INtegrated Query)
  • Client-side JavaScript
  • Node.js
  • Python

If you have previous experience working with other NoSQL databases, such as MongoDB, you will be surprised with the lack of a complete administration Web console and an interactive shell. The Microsoft Azure portal allows you to create a new DocumentDB account and resource group, configure the service features, check both usage and billing statistics, access tutorials and SDKs, and get the URI and keys to access the database service. It is necessary to use either the SDK or the REST API to create and manage databases, collections, documents and users. Thus, if you want to create a database, a collection, or add some JSON documents and run a few queries, you need to select your favorite language and platform from the aforementioned list and start coding. For this article, I'll use the .NET SDK and provide examples using C#.

The .NET SDK provides classes for each DocumentDB task with many asynchronous methods, which end with the Async suffix . Microsoft .NET Framework 4.5 introduced asynchronous methods. If you still haven't worked with the async and await modifiers in C#, you can read Using Asynchronous Methods in ASP.NET 4.5 and in MVC 4 to come up to speed.

Getting Started

Create a new Visual C# Windows Console application project in Visual Studio 2013. Then, run the following command within the Package Manager Console.

Install-Package Microsoft.Azure.Documents.Client -Pre

The -Pre option indicates that you want to install a pre-release package. At the time of writing, the latest version for Microsoft.Azure.Documents.Client was 0.9.0-preview. You can check the latest version and the version history for this NuGet package online.

If you check the References for the project within Solution Explorer, you will notice the installation added the Microsoft.Azure.Documents.Client assembly and its necessary dependency, Newtonsoft.Json. The installation also adds a new packages.config file to the project with the following lines:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Microsoft.Azure.Documents.Client" version="0.9.0-preview" targetFramework="net45" />
  <package id="Newtonsoft.Json" version="4.5.11" targetFramework="net45" />
</packages>

Before adding C# code, you need to sign in to the Azure management preview portal and create a new DocumentDB account. Just click NEW , select DocumentDB , and enter the desired name and the desired configuration based on your needs. The default 1 Capacity Unit configuration will be enough for the sample code. After you click the Create button and Azure finishes creating the DocumentDB account, you can click on its tile and get your URI and key. Copy both the URI and Primary key values. I'll use https://example.com:443/ for the URI and DocumentDBSample for the Primary key. However, these values won't allow you to test any valid DocumentDB service and you will need to configure your own values.

Now, go back to Visual Studio 2013 and add a reference to System.Configuration. I don't want to store credentials in source code. So, it's necessary to use the URI, also known as endpoint URL, and the authorization key to access the DocumentDB service. Add an Application Configuration File item named appSetting.config to the project, specify the URI value in the EndPointUrl key, and the primary key value in the AuthorizationKey key. The following lines show the contents for the appSetting.config file with "https://example.com:443/" as the EndPointUrl value and "DocumentDBSample" as the AuthorizationKey value. Don't forget to replace these values with the values you copied from the Azure management portal. The DatabaseId and CollectionId keys define the ids for the database and the collection that the code is going to create and use.

<appSettings>
  <!-- Replace the value with the value you copied from the Azure management portal -->
  <add key="EndPointUrl" value="https://example.com:443/"/>
  <!-- Replace the value with the value you copied from the Azure management portal -->
  <add key="AuthorizationKey" value="DocumentDBSample"/>
  <add key="DatabaseId" value="Entertainment"/>
  <add key="CollectionId" value="Games"/>
</appSettings>

Open the App.config file and add the following line in the configuration section. This way, you specifiy that the appSettings file name is appSettings.config.

<appSettings file="appSettings.config" />

The following lines show the code for Program.cs, which uses the specified settings to use the Azure DocumentDB service. The code creates an Entertainment database and a Games collection in this database. Then, the code adds two documents to the Games collection, and finally uses DocumentDB SQL to query the collection and retrieve one of the previously saved documents.

using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Microsoft.Azure.Documents.Linq;

namespace DocumentDB
{
    class Program
    {
        // The DocumentClient instance that allows the code to run methods that interact with the DocumentDB service
        private static DocumentClient client;
        // Retrieve the desired database id (name) from the configuration file
        private static readonly string databaseId = ConfigurationManager.AppSettings["DatabaseId"];
        // Retrieve the desired collection id (name) from the configuration file
        private static readonly string collectionId = ConfigurationManager.AppSettings["CollectionId"];
        // Retrieve the DocumentDB URI from the configuration file
        private static readonly string endpointUrl = ConfigurationManager.AppSettings["EndPointUrl"];
        // Retrieve the DocumentDB Authorization Key from the configuration file
        private static readonly string authorizationKey = ConfigurationManager.AppSettings["AuthorizationKey"];

        public static void Main(string[] args)
        {
            try
            {
                using (client = new DocumentClient(new Uri(endpointUrl), authorizationKey))
                {
                    RunAsync().Wait();
                }
            }
            catch (DocumentClientException de)
            {
                Exception baseException = de.GetBaseException();
                Console.WriteLine("Status code {0} error occurred: {1}, Message: {2}", de.StatusCode, de.Message, baseException.Message);
            }
            catch (Exception e)
            {
                Exception baseException = e.GetBaseException();
                Console.WriteLine("Error: {0}, Message: {1}", e.Message, baseException.Message);
            }
            finally
            {
                Console.WriteLine("Please, press any key.");
                Console.ReadKey();
            }
        }

        private static async Task<Database> RetrieveOrCreateDatabaseAsync(string id)
        {
            // Try to retrieve the database (Microsoft.Azure.Documents.Database) whose Id is equal to databaseId            
            var database = client.CreateDatabaseQuery().Where(db => db.Id == databaseId).AsEnumerable().FirstOrDefault();

            // If the previous call didn't return a Database, it is necessary to create it
            if (database == null)
            {
                database = await client.CreateDatabaseAsync(new Database { Id = databaseId });
                Console.WriteLine("Created Database: id - {0} and selfLink - {1}", database.Id, database.SelfLink);
            }

            return database;
        }

        private static async Task<DocumentCollection> RetrieveOrCreateCollectionAsync(string databaseSelfLink, string id)
        {
            // Try to retrieve the collection (Microsoft.Azure.Documents.DocumentCollection) whose Id is equal to collectionId
            var collection = client.CreateDocumentCollectionQuery(databaseSelfLink).Where(c => c.Id == id).ToArray().FirstOrDefault();
            
            // If the previous call didn't return a Collection, it is necessary to create it
            if (collection == null)
            {
                collection = await client.CreateDocumentCollectionAsync(databaseSelfLink, new DocumentCollection { Id = id });
            }

            return collection;
        }

        private static async Task CreateGameDocumentsAsync(string collectionSelfLink)
        {
            // Create a dynamic object
            dynamic dynamicGame1 = new
            {
                gameId = "1",
                name = "Cookie Crush in Mars",
                releaseDate = new DateTime(2014, 8, 10),
                categories = new string[] { "2D", "puzzle", "addictive", "mobile", "in-game purchase" },
                played = true,
                scores = new []
                {
                    new {
                        playerName = "KevinTheGreat",
                        score = 10000
                    },
                    new {
                        playerName = "BrandonGamer",
                        score = 5800
                    },
                    new {
                        playerName = "VanessaWonderWoman",
                        score = 10000
                    },
                }
            };

            var document1 = await client.CreateDocumentAsync(collectionSelfLink, dynamicGame1);

            // Create a dynamic object
            dynamic dynamicGame2 = new
            {
                gameId = "2",
                name = "Flappy Parrot in Wonderland",
                releaseDate = new DateTime(2014, 7, 10),
                categories = new string[] { "mobile", "completely free", "arcade", "2D" },
                played = true,
                scores = new[]
                {
                    new {
                        playerName = "KevinTheGreat",
                        score = 300
                    }
                },
                levels = new[] 
                {
                    new {
                        title = "Stage 1",
                        parrots = 3,
                        rocks = 5,
                        ghosts = 1
                    },
                    new {
                        title = "Stage 2",
                        parrots = 5,
                        rocks = 7,
                        ghosts = 2
                    }
                }
            };

            var document2 = await client.CreateDocumentAsync(collectionSelfLink, dynamicGame2);
        }

        private static async Task RunAsync()
        {
            // Try to retrieve a Database if exists, else create the Database
            var database = await RetrieveOrCreateDatabaseAsync(databaseId);

            // Try to retrieve a Document Collection, else create the Document Collection
            var collection = await RetrieveOrCreateCollectionAsync(database.SelfLink, collectionId);

            // Create two documents within the recently created or retrieved Game collection
            await CreateGameDocumentsAsync(collection.SelfLink);

            // Use DocumentDB SQL to query the documents within the Game collection
            var game1 = client.CreateDocumentQuery(collection.SelfLink, "SELECT * FROM Games g WHERE g.gameId = \"1\"").ToArray().FirstOrDefault();
            if (game1 != null)
            {
                Console.WriteLine("Game with Id == \"1\": {0}", game1);
            }
        }
    }
}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 


Video