Skip to main content

Saving State data with BotBuilder-Azure in .NET

The Bot Framework Connector State Service was created to ensure that bots built using the Microsoft Bot Framework can keep track of conversation state while the bot itself is stateless. The state service enables your bot to store and retrieve user data, conversation data, a conversation, or a specific user within the context of a specific conversation. A default implementation, using the Connector State Service, is automatically provided by the Node.js and .NET SDKs. However, the state service only allows limited access to this state information. One of the most frequent questions we’ve been receiving from customers lately is how bot developers can have more direct control over a bot’s state data.

The default Connector State Service is not intended for production applications. Bots should use one of the Azure Extensions available on Github. Or, implement a custom State Client using whatever data storage platform you choose.

In this article we will demonstrate how to use the BotBuilder Azure nuget package, which will allow you to easily integrate your bots with Azure Table Storage, and DocumentDB.

Don’t worry, in the future we’ll also explore possible implementations in Node.js, as well as using other databases.

Skip to AzureTable

Skip to DocumentDB

Prerequisites

Install Nuget packages

You’ll need an Azure Account to use either of the Azure extensions that the nuget package supports, click here to sign up for a free trial.

Open an existing bot, or create a new one using the Bot Template. Next, install the (2) following nuget packages:

Using Azure Table Storage

Here we summarize creating the Azure Table storage where the bot’s state data will be saved to, then we’ll discuss how to configure a bot to call that service.

Setting up Azure Table

Once you’ve logged into Azure, you’ll need to create a new Azure Table storage service. Similar to launching other Azure features, hit the New button on the top left. Search for Storage account, which implements Azure table.

Fill in the appropriate fields, then hit the blue Create button at the bottom when you’re ready to deploy the new storage service.

Once your new Storage service is finished deploying, it will display the overview with all of the features and options available as below:

Select the Access keys tab and copy the connection string to use later. This connection string is what will be used by your bot to call the storage service to save state data.

Note: The key will automatically be provided in the connection string generated by Azure

Configuring the Bot for Azure Table

Click here for the full Azure Table bot sample on hosted Github. The sample is just a simple echo bot, similar to the getting started sample for .NET, only this sample leverages the BotBuilder-Azure nuget package to save state data instead of using the default state service.

Next, go into your project’s Global.asax.cs and create a new reference variable to a new instance of the TableBotDataStore class, which implements the IBotDataStore<BotData>interface. This interface is what allows you to override the default state service connection. Then, we need to update the container by registering the service as shown below.

...
using Autofac.Integration.WebApi;
using Microsoft.Bot.Builder.Azure;
...
var store = new TableBotDataStore(ConfigurationManager.ConnectionStrings["StorageConnectionString"].ConnectionString); 

builder.Register(c => store)
    .Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
    .AsSelf()
    .SingleInstance();

builder.Register(c => new CachingBotDataStore(store,
        CachingBotDataStoreConsistencyPolicy
        .ETagBasedConsistency))
        .As<IBotDataStore<BotData>>()
        .AsSelf()
        .InstancePerLifetimeScope();    

builder.Update(Conversation.Container);

Now that we’ve created a new connection to a custom state service, we’ve registered that service to the bot’s container. Lastly, we need to update Web.config to include the connection string to the Azure table storage which we created earlier.

<connectionStrings>
  <add name="StorageConnectionString" 
  connectionString="Your Azure Connection String"/>
</connectionStrings>

With the Global.asx.cs and Web.config files now modified, when you run your bot in Visual Studio, the botdata table will be created. Then, using the Azure Storage explorer, we can query the Azure table we created – there’s our Bot’s state data!

Using DocumentDB

Note: DocumentDB was recently rebranded as CosmosDB – Click Here for Details

Here we’ll brief over how to setup an Azure DocumentDB storage service to store a bot’s state data. Login to the Azure portal, select New from the top left corner menu, then under databases, select Azure Cosmos DB. Make sure to select SQL (DocumentDB) from the API dropdown, and fill the other fields accordingly.

When your database service is finished deploying, it will display the overview with all of the features and options available as below:

Select “Keys” from the left menu from the overview, and it will display authorization credentials for your Cosmos DB database. You’ll need to provide your both with these credentials (URI and primary key) later.

Configuring the Bot for DocumentDB

Click here for the full Azure DocumentDB bot sample on hosted Github. The sample is just a simple echo bot, similar to the getting started sample for .NET, only this sample leverages the BotBuilder-Azure nuget package to save state data instead of using the default state service.

We’ve created a DocumentDB (CosmosDB) instance in Azure, now it’s time to connect it to a bot. Open your bot’s Global.asax.cs and implement the following:

var builder = new ContainerBuilder();

var uri = new Uri(ConfigurationManager.AppSettings["DocumentDbUrl"]);
var key = ConfigurationManager.AppSettings["DocumentDbKey"];

var store = new DocumentDbBotDataStore(uri, key);

builder.Register(c => store)
    .Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
    .AsSelf()
    .SingleInstance();

builder.Register(c => new CachingBotDataStore(store,
                                              CachingBotDataStoreConsistencyPolicy.ETagBasedConsistency))
    .As<IBotDataStore<BotData>>()
    .AsSelf()
    .InstancePerLifetimeScope();

builder.Update(Conversation.Container);

Above, all we’re doing is creating two new reference variables, uri and key which as you may have guessed, use the DocumentDB’s URI and Key which we’ll configure in the bot’s Web.configlater. The uri and key are used to create a new DocumentDbBotDataStore, then we register the database to the bot’s container.

Going into Web.config, we provision the actual DocumentDB URI and Key here. You can find this information under the Keys tab of your DocumentDB overview on the Azure portal.

<add key="DocumentDbUrl" value="Your DocumentDB URI"/>
<add key="DocumentDbKey" value="Your DocumentDB Key"/>

Once both the Global.asx.cs and Web.config files in the bot are configured, we’re ready to start the bot to test out our bot with the DocumentDB storage we created. Following this sample, we start the bot in Visual Studio and test it using the Bot Framework emulator as shown:

Back in the Azure portal, we select the Data Explorer feature to verify that the state information from our bot is being saved. Looks like our state data was successfully saved!

Why use a Custom State Client

Using the default state service, you are limited to using the bot state methods to manage state data.

Reasons to use custom state storage:

  • higher state API throughput (more control over performance)
  • lower-latency for geo-distrubtion
  • control over where the data is stored
  • access to the actual state data
  • store more than 32kb

Summary

In this article we introduced the BotBuilder-Azure nuget package and demonstrated how it can be used to store your bot’s state data in both Azure Table, and DocumentDB (recently rebranded CosmosDB) in lieu of the default connector state service provided by the BotBuilder SDK. Hopefully, we’ve addressed a lot of the questions we’ve received from the bot developer community in addition to demonstrating how easy using Azure can be. In future posts, we’ll explore other ways to manage your bot’s state data, and demonstrate how to create custom state clients for Node.js bots as well.

Happy Making!

Eric Dahlvang and Matthew Shim from the Bot Framework team.

References