(Print this page)

Using Azure DocumentDB from a .Net application
Published date: Tuesday, June 16, 2015
On: Moer and Éric Moreau's web site

I see more and more interest in Azure but very often developers complained that examples fall short on real task. I have seen many examples creating a Microsoft Azure DocumentDB database, a collection, a document before deleting everything.

This is far from being real life scenario!

So here is my own attempt at creating a bit more realistic scenario.

What is DocumentDB?

DocumentDB is one technology offering a NoSQL document database service in Azure (Table storage is another one). The data is stored in the form of a Json document.

Even if it seems new to you, Microsoft has been testing it for quite some time now. The Microsoft OneNote application which you are surely using persist your documents using this technology.

Demo code and pre-requisite

This month code is provided both in VB and C#. The solution has been created using Visual Studio 2015 RC and can be opened as-is using Visual Studio 2013 (update 4 or better).

As you can see in figure 1, the example is in the form of a Windows Forms application letting you enter an item Id and a value to save them in the DocumentDB. It also let you delete the selected item.

To be able to run the code, you will need an access to an Azure account. If you don’t currently have one, you can start a trial by visiting http://azure.microsoft.com/en-us/pricing/free-trial/.

Figure 1: Demo application in action

Creating the DocumentDB storage

Before starting with the code, we need to provision a DocumentDB service.

Open the new Microsoft Azure portal (still in preview at the time of writing this). Note that the old portal cannot be used with DocumentDB.

Click the New button, navigate to “Data + Storage” and select “Azure DocumentDB” from the list as shown in figure 2.

Figure 2: Creating a new Azure DocumentDB in the portal

After you clicked “Azure DocumentDB”, another blade open (as shown in figure 3). This last blade let you enter the ID (or the name if you prefer) of your DocumentDB. This name has to be unique. You should also select a resource group that will make sense with your application (or create a new one). After you clicked the OK button on this blade, the DocumentDB will get created in your Azure account. This last operation can take up to 10 minutes before your storage gets ready.

Figure 3: Setting the DocumentDB properties

Creating the project

We don’t have to wait for the Azure DocumentDB to get ready, we can start working on the code.

Open Visual Studio and create a new project (or just download and open my demo solution). The projects I have created are Windows Forms project.

The very first thing that needs to be added to the new project is a reference to the “Microsoft Azure DocumentDB Client Library”. The easiest way to get it is by adding it through the NuGet packages (right click your project and select Manage Nuget Packages).

Figure 4: Installing the package from NuGet

We will also define 4 variables at the top of the form because these values will be reused in many places.

The first 2 ones are easy. You need to find a name for your database and another one for your collection (a collection can contain multiple collections):

Private Shared databaseId As String = "DatabaseOfItemsVB"
Private Shared collectionId As String = "CollectionOfItemsVB"

You should modify these 2 Ids to reflect the data that you want to store.

The other 2 variables are defining the URL and the secret key for your account like this:

Private Shared EndpointUrl As String = "https://YourDocDBURI.documents.azure.com:443/"
Private Shared AuthorizationKey As String = "YourKeyGoesHere"

By now, your DocumentDB is surely created. Open its blade and click the “All settings” button. From the All Settings blade, click the Keys button to open another blade. Now copy the content of the URI to paste the value to your EndpointUrl variable and copy the Primary key to your AuthorizationKey variable. Without these values, you will never be able to connect to your DocumentDB.

Figure 5: DocumentDB settings

Creating the database

The very first thing that needs to be created by code when we want to use a Document database is the Database itself and at least a first collection.

The database that we will create will be stored in the DocumentDB storage we just provided from the Azure portal. The Azure DocumentDB refers to this storage as a DocumentClient object therefore we often name the instance simply as “client”. Getting an instance of this client is as simple as writing this single line of code which I have wrapped in a method because we often needs it:

Private Shared Function GetDocDbClientInstance() As DocumentClient
    ' Create a new instance of the DocumentClient.
    Return New DocumentClient(New Uri(EndpointUrl), AuthorizationKey)
End Function

Notice here that 2 of the variables we defined earlier are used.

Now that we have our DocumentClient instance, we can create our database or return its instance if it is already existing. Once again, this Database instance will be used many times so I have created another method:

Private Shared Async Function GetDocDbDatabaseInstance(client As DocumentClient) As Task(Of Database)
    Try
        ' Try to retrieve the database (Microsoft.Azure.Documents.Database) whose Id is equal to databaseId            
        Dim database = client.CreateDatabaseQuery().AsEnumerable().ToList().FirstOrDefault(Function(db) db.Id = databaseId)

        ' If the previous call didn't return a Database, it is necessary to create it
        If database Is Nothing Then
            database = Await client.CreateDatabaseAsync(New Database() With {.Id = databaseId})
            MessageBox.Show(String.Format("Created Database: id - {0} and selfLink - {1}", database.Id, database.SelfLink))
        End If

        Return database
    Catch ex As Exception
        Dim baseException As Exception = ex.GetBaseException()
        MessageBox.Show(String.Format("Error: {0}, Message: {1}", ex.Message, baseException.Message))
        Return Nothing
    End Try
End Function

This method queries the DocumentClient instance to see if the databaseId (the databaseId variable we created earlier) is existing. If it is not existing, a new database with this name is created.

Finally, a database contains one or more collection of documents. In order to get the instance of an existing collection (or to create it if not already existing), I have written this method:

Private Shared Async Function GetDocDbCollectionInstance(client As DocumentClient, database As Database) As Task(Of DocumentCollection)
    Try
        ' Try to retrieve the collection (Microsoft.Azure.Documents.DocumentCollection) whose Id is equal to collectionId
        Dim collection = client.CreateDocumentCollectionQuery(database.CollectionsLink).ToArray().
            FirstOrDefault(Function(c) c.Id = collectionId)

        ' If the previous call didn't return a Collection, it is necessary to create it
        If collection Is Nothing Then
            collection = Await client.CreateDocumentCollectionAsync(database.CollectionsLink,
                                                                    New DocumentCollection() With {.Id = collectionId})
        End If

        Return collection
    Catch ex As Exception
        Dim baseException As Exception = ex.GetBaseException()
        MessageBox.Show([String].Format("Error: {0}, Message: {1}", ex.Message, baseException.Message))
        Return Nothing
    End Try
End Function

This method is using the last variable (collectionId) we have defined.

If you can run these 3 methods, you are properly connected to your DocumentDB Storage. To prove it, return to the Azure Portal and click the “Document Explorer” icon. A new blade will open and you should see the name of your variables in the Database name and the Collection name. Because no documents have been created yet, the list below is empty.

Figure 6: Using the Document Explorer to see that the Database and Collection have been created

Later, when you will have created some documents, you will be able to return to the Document Explorer blade and hit the Refresh button. The list of ids will then be shown. By clicking any of these ids, a blade containing the Json document will be shown.

Saving documents

We are to the point where it would be appropriate to save our first document. As I already said, the documents are in the form of Json documents. The easiest way to get this API to work is by creating a class with some properties. This class will be serialize to Json before being saved in the cloud. It will also be possible to rehydrate a class instance by de-serializing the Json string from the cloud.

So I have created a very small class with a few properties to match the UI of figure 1:

Class Item
    <JsonProperty(PropertyName:="id")>
    Public Property Id As String
    Public Property Value As String
    Public Property LastUpdate As DateTime
End Class

There is only 1 thing particular in that class. Because document requires an attribute name id and I wanted my property to be named Id, I had to set a JsonProperty attribute to my property so the serializing handles that properly.

Now that I have this class, I can create an instance and feed it with the content of the textboxes I have in my demo application:

Dim item = New Item() With {
    .Id = txtId.Text,
    .Value = txtValue.Text,
    .LastUpdate = DateTime.Now
}

We are now ready to call a method to save this document:

Private Shared Async Function SaveItem(item As Item, oldId As String) As Task
     Try
         Dim client As DocumentClient = GetDocDbClientInstance()
         Dim database As Database = Await GetDocDbDatabaseInstance(client)
         Dim documentCollection As DocumentCollection = Await GetDocDbCollectionInstance(client, database)

         Await DeleteItem(oldId)
         Await client.CreateDocumentAsync(documentCollection.DocumentsLink, item)
     Catch ex As Exception
         Dim baseException As Exception = ex.GetBaseException()
         MessageBox.Show(String.Format("Error: {0}, Message: {1}", ex.Message, baseException.Message))
     End Try
End Function

The first 3 lines are calling the 3 methods described in the “Creating the database” section. The document has to be saved in a collection of our database.

The real saving is done by the call to the CreateDocumentAsync. As you can see, we just pass our instance of item and the serialization will be automatically handled for us.

In the current version of the API, there is no update method. In place of this, I created a DeleteItem (explained later) method that I use before trying to save the item. So it is the equivalent of always inserting. To be able to delete the existing item (if any), I need its key (or id if you prefer). As we will find later, when I read a document, I store its id in the Tag property of the textbox so I have it handy when comes time to update and/or delete it.

Listing documents

After each operation in my demo application, I call a method to fill the listbox control with the existing item. This method reads like this:

Private Async Function FillListBox() As Task
    Dim client As DocumentClient = GetDocDbClientInstance()
    Dim database As Database = Await GetDocDbDatabaseInstance(client)
    Dim documentCollection As DocumentCollection = Await GetDocDbCollectionInstance(client, database)

    listBox1.Items.Clear()

    Dim items = client.CreateDocumentQuery(documentCollection.DocumentsLink).ToList()

    For Each item In items
        listBox1.Items.Add(item.Id)
    Next
End Function

The CreateDocumentQuery from the API is used to retrieve the list of existing documents. We can then loop through this list to add the Id property (or anything you would like) to the list box.

When you click an item in the list box, you surely want to fill the textboxes below the listbox. The SelectedValueChanged event called the GetItem method:

Private Shared Async Function GetItem(id As String) As Task(Of Item)
    Try
        Dim client As DocumentClient = GetDocDbClientInstance()
        Dim database As Database = Await GetDocDbDatabaseInstance(client)
        Dim documentCollection As DocumentCollection = Await GetDocDbCollectionInstance(client, database)

        Dim item = client.CreateDocumentQuery(Of Item)(documentCollection.DocumentsLink,
                                                       String.Format("SELECT * FROM CollectionOfItems i WHERE i.id = ""{0}""", id)).
                                                   ToList().FirstOrDefault()
        Return item
    Catch ex As Exception
        Dim baseException As Exception = ex.GetBaseException()
        MessageBox.Show([String].Format("Error: {0}, Message: {1}", ex.Message, baseException.Message))
        Return Nothing
    End Try
End Function

This method use the CreateDocumentQuery (the same method we used to list all items) but this time we pass a query very similar to the SQL syntax to retrieve only the one interesting to us (the one with the Id we clicked on).

The code inside the SelectedValueChanged event looks like this:

Dim id As String = listBox1.SelectedItem.ToString()
Dim item As Item = Await GetItem(id)
txtId.Text = item.Id
txtId.Tag = item.Id  ' keep the original key
txtValue.Text = item.Value

Again, the Id is saved into the Tag property to be used by the Save and Delete commands.

Deleting documents

The DeleteItem method reads like this:

Private Shared Async Function DeleteItem(oldId As String) As Task
    Try
        Dim client As DocumentClient = GetDocDbClientInstance()
        Dim database As Database = Await GetDocDbDatabaseInstance(client)
        Dim documentCollection As DocumentCollection = Await GetDocDbCollectionInstance(client, database)

        Dim items = client.CreateDocumentQuery(Of Document)(documentCollection.DocumentsLink,
                                               String.Format("SELECT * FROM CollectionOfItemsVB i WHERE i.id = ""{0}""", oldId))
        If items IsNot Nothing Then
            For Each i As Document In items
                Await client.DeleteDocumentAsync(i.SelfLink)
            Next
        End If
    Catch ex As Exception
        Dim baseException As Exception = ex.GetBaseException()
        MessageBox.Show([String].Format("Error: {0}, Message: {1}", ex.Message, baseException.Message))
    End Try
End Function

In this method, we recognize code from the GetItem (the query to find the item) before calling the DeleteDocumentAsync method from the API passing the SelfLink of the item we found. SelfLink is a property of a document that contains a kind of URL to this particular document.

Deleting the database

The last feature of my demo application is to delete the full database which is all the documents in all the collections of the DocumentDB database.

Much like when we wanted to delete a single document, we were required to find the SelfLink property of the document, we now need to provide the SelfLink property of the database itself and pass it to the DeleteDatabaseAsync method of the API.

Private Shared Async Function DeleteDatabase() As Task
    Try
        Dim client As DocumentClient = GetDocDbClientInstance()
        Dim database As Database = Await GetDocDbDatabaseInstance(client)

        ' Clean up/delete the database
        Await client.DeleteDatabaseAsync(database.SelfLink)
    Catch ex As Exception
        Dim baseException As Exception = ex.GetBaseException()
        MessageBox.Show([String].Format("Error: {0}, Message: {1}", ex.Message, baseException.Message))
    End Try
End Function

If you now go back to the Document Explorer blade of the Azure portal, you will find that everything has disappeared.

Conclusion

Microsoft provided a fairly simple API to let us use DocumentDB from any type of application. I just proved it here by using it from a Windows Forms application!

I hope this article will give you some interest to dig more on the subject. You can find more information in the official documentation.


(Print this page)