ben
ben

Reputation: 401

Custom IBotDataStore breaks context.PrivateConversationData

I am using Bot Builder 3.8.5.

As specified on the Microsoft Bot Framework documentation here:

By default, the Bot Framework SDK for .NET stores state data using the Bot Framework State service, which is intended for prototyping only and is not designed for use by bots in a production environment. For performance and security reasons in the production environment, you should either use the Bot Builder SDK Azure Extensions to store state data in your own Azure Table storage or Azure DocumentDB store or create a custom implementation of IBotDataStore to store state data in the destination that you specify.

I decided to use the Bot builder extension with Azure Table storage. The state is correctly saved into the table storage.

However, I have the following issue :

In MessageController.cs I am adding a private conversation data string as below (as I don't have access to the IDialogContext):

StateClient stateClient = activity.GetStateClient();
BotData userData = await stateClient.BotState.GetPrivateConversationDataAsync(activity.ChannelId, activity.Conversation.Id, activity.From.Id);
userData.SetProperty<string>("MyKey", "MyValue"); 
await stateClient.BotState.SetPrivateConversationDataAsync(activity.ChannelId, activity.Conversation.Id, activity.From.Id, userData);

Then in my RootDialog file I am trying to extract this data by doing below:

context.PrivateConversationData.TryGetValue("MyKey", out myValue);

but the context.PrivateConversationData does not contain this key, it always returns null.

Note : it was perfectly working when I was using the default Bot State service...

If I use the following code, it works but it has major impact on performance (the call to retrieve the botdata is very slow):

var stateClient = context.Activity.GetStateClient();
BotData pConversationData = context.Activity.GetStateClient().BotState.GetPrivateConversationDataAsync( context.Activity.ChannelId, context.Activity.Conversation.Id, context.Activity.From.Id).GetAwaiter().GetResult();
return pConversationData.GetProperty<T>("MyKey");

What am I doing wrong ?

UPDATE

It seems that overriding the IBotDataStore (with the bot builder extension) works for context.UserData but not for the StateClient.BotState... In fact, I think I need to have a custom implementation for IBotState too to be able to access the BotState outside of an IDialog..

Upvotes: 0

Views: 595

Answers (2)

ben
ben

Reputation: 401

I think I found a way to overcome this problem. The issue is that if you want to access the Bot state outside an IDialog, you dont have access to the IDialogContext and context.PrivateConversationData or context.UserData which are handling the load / save / flush of the bot state.

StateClient can be used if you are using the default bot state service but when you have a custom IBotDataStore (as recommended on the bot framework documentation), you can not use the default StateClient as it is still pointing to the default state service. I suppose one solution could be to create a custom IStateClient / IBotState.

Instead of that, I used the following code in my MessageController :

using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
        var botData = scope.Resolve<IBotData>();
        await botData.LoadAsync(default(CancellationToken));
        botData.PrivateConversationData.SetValue<T>("MyKey", value);
        await botData.FlushAsync(default(CancellationToken));
}

With this code, I was able to save / load data in my custom IBotDataStore.

Hope this helps!

Upvotes: 2

Nicolas R
Nicolas R

Reputation: 14619

When setting your userData value here:

StateClient stateClient = activity.GetStateClient();
BotData userData = await stateClient.BotState.GetPrivateConversationDataAsync(activity.ChannelId, activity.Conversation.Id, activity.From.Id);
userData.SetProperty<string>("MyKey", "MyValue"); 

Add the following to force the data to be updated:

await stateClient.BotState.SetPrivateConversationDataAsync(activity.ChannelId, activity.Conversation.Id, activity.From.Id, userData);

Then it should be correctly stored and accessible

Upvotes: 1

Related Questions