Reputation: 115
I am trying to build a chatbot with nested dialogs which is supposed to gather information from the user and store it in Azure CosmosDB. The dialog worked fine until I implemented the Cosmos DB storage. Now, with the CosmosDB storage the dialog loops on the first task in the first dialog, instead of continuing. How can I solve this problem?
Beginning with the dialogs, and how it was before implementing CosmosDB storage. I basically followed the code in this sample 43.complex-dialog.
Then, implementing the storage I used this answer as a guide. I set up the cosmosDB storage in Startup.cs like this:
public class Startup
{
private const string CosmosServiceEndpoint = "MyCosmosServiceEndpoint";
private const string CosmosDBKey = "MyCosmosDBKey";
private const string CosmosDBDatabaseName = "MyCosmosDBDatabaseName";
private const string CosmosDBCollectionName = "MyCosmosDBCollectionName";
private const string CosmosDBPartitionKey = "MyCosmosDBPartitionKey";
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var storage = new CosmosDbStorage(new CosmosDbStorageOptions
{
AuthKey = CosmosDBKey,
CollectionId = CosmosDBCollectionName,
CosmosDBEndpoint = new Uri(CosmosServiceEndpoint),
DatabaseId = CosmosDBDatabaseName,
PartitionKey = CosmosDBPartitionKey
});
var conversationState = new ConversationState(storage);
var userState = new UserState(storage);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
services.AddSingleton<MainDialog>();
services.AddTransient<IBot, WelcomeBot<MainDialog>>();
services.AddSingleton<IStorage, MemoryStorage>();
services.AddSingleton(userState);
services.AddSingleton(conversationState);
services.AddSingleton(userState.CreateProperty<UserProfile>("MyUserState"));
services.AddSingleton(conversationState.CreateProperty<DialogState>("MyBotDialogState"));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseMvc();
}
}
}
My main bot looks like this (don't mind the name "Echobot"):
public class EchoBot<T> : ActivityHandler where T : Dialog
{
private readonly BotState _userState;
private readonly BotState _conversationState;
private readonly Dialog _dialog;
private readonly ILogger _logger;
private readonly IStatePropertyAccessor<UserProfile> _userStateAccessor;
private readonly IStatePropertyAccessor<DialogState> _conversationDialogStateAccessor;
// Create cancellation token (used by Async Write operation).
public CancellationToken CancellationToken { get; private set; }
public EchoBot(ConversationState conversationState, UserState userState, T dialog, ILogger<EchoBot<T>> logger, IStatePropertyAccessor<UserProfile> userStatePropertyAccessor, IStatePropertyAccessor<DialogState> dialogStatePropertyAccessor)
{
_conversationState = conversationState;
_userState = userState;
_dialog = dialog;
_logger = logger;
_userStateAccessor = userStatePropertyAccessor;
_conversationDialogStateAccessor = dialogStatePropertyAccessor;
}
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
{
await base.OnTurnAsync(turnContext, cancellationToken);
var currentUserState = await _userStateAccessor.GetAsync(turnContext, () => new UserProfile(), cancellationToken);
var currentConversationDialogState = await _conversationDialogStateAccessor.GetAsync(turnContext, () => new DialogState(), cancellationToken);
await _userStateAccessor.SetAsync(turnContext, currentUserState, cancellationToken);
await _conversationDialogStateAccessor.SetAsync(turnContext, currentConversationDialogState, cancellationToken);
// Save any state changes that might have occured during the turn.
await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await _userState.SaveChangesAsync(turnContext, false, cancellationToken);
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken=default)
{
_logger.LogInformation("Running dialog with Message Activity.");
//See DialogExtension.cs in the sample "complex-dialog" to see the Run method.
await _dialog.Run(turnContext, _conversationDialogStateAccessor, cancellationToken);
}
}
}
Upvotes: 1
Views: 295
Reputation: 7241
For now, it should work if you remove the PartitionKey
parameter from CosmosDbStorageOptions
. You will likely need to delete your Container or use a different name, since yours is currently partitioned. Easiest to just delete your Container and let the bot make one for you.
There's currently a bug in all the Bot Builder SDKs around reading from partitioned databases when the partitionKey is supplied. Tracking the issue here
Upvotes: 2