Reputation: 10208
I'm a few days in on the Bot Framework so very new. I've been trying to arrive at an understanding of managing the state and to be honest I'm just not getting it. It seems the framework and advice on this has changed a lot recently and there doesn't appear to be any clear advice or samples.
This page Says:
Save the information to bot state. This would require you to design your dialog to have access to the bot's state property accessors.
But there are no examples of how to achieve this.
The last step in one of my Waterfall dialogs looks like this:
AddStep(async (stepContext, cancellationToken) =>
{
var response = stepContext.Result as FoundChoice;
stepContext.Values["maxPrice"] = response;
return await stepContext.BeginDialogAsync(SearchDialog.Id, null, cancellationToken);
});
It's basically kicking off a new dialog and I want to either pass the collected data from this dialog into the SearchDialog
either by passing the object or, preferably, saving this into my BotAccessors
and then the SearchDialog
retrieving this and using it.
All MS examples have waterfall steps defined as async
methods on the IBot
class. Which also isn't how they recommend putting bot dialogs together making the example pretty useless all in all.
Also, it seems that even the Microsoft v4 docs are out of date, such as this doc, that is still telling us to use deprecated code, such as:
options.State.Add(new ConversationState(storage));
Unfortunately it seems the docs are more confusing than helpful on this topic at the moment. What's the best way to manage this state?
Upvotes: 3
Views: 2289
Reputation: 12274
Note: The Basic Bot sample has been replaced by the Core Bot sample so this answer is outdated
Have a look at the way a basic bot is set up either in the samples repo or by creating a basic bot from the template in Azure.
The state property accessor is declared in the BasicBot
class:
private readonly IStatePropertyAccessor<GreetingState> _greetingStateAccessor;
It is then assigned to in the BasicBot
constructor:
_greetingStateAccessor = _userState.CreateProperty<GreetingState>(nameof(GreetingState));
It is then passed to the GreetingDialog
constructor:
Dialogs.Add(new GreetingDialog(_greetingStateAccessor, loggerFactory));
It is then assigned to a property of the GreetingDialog
class:
UserProfileAccessor = userProfileStateAccessor ?? throw new ArgumentNullException(nameof(userProfileStateAccessor));
It is then used in many places throughout the GreetingDialog
class with the GetAsync
and SetAsync
methods. For example:
var greetingState = await UserProfileAccessor.GetAsync(stepContext.Context, () => null);
Upvotes: 4
Reputation: 436
Last two steps from Waterfall Dialog may look something like the following:
public async Task<DialogTurnResult> AskForLocation(WaterfallStepContext sc, CancellationToken cancellationToken)
{
// the previous step asked for the Email so now bot is going to save it in botstate
_state = await _accessor.GetAsync(sc.Context, () => new MyApplicationState());
var email = _state.Email = (string)sc.Result;
// this is not in the template because it is saving in a different manner
// just being explicit about saving here
await _accessor.SetAsync(sc.Context, _state);
await sc.Context.SendActivityAsync("Got your email!");
var prompt = new PromptOptions
{
Prompt = MessageFactory.Text($"Please specify location."),
};
return await stepContext.PromptAsync(locationPrompt, prompt);
}
public async Task<DialogTurnResult> FinishDialog(WaterfallStepContext sc, CancellationToken cancellationToken)
{
_state = await _accessor.GetAsync(sc.Context);
_state.Location = (string)sc.Result;
// save location this time
await _accessor.SetAsync(sc.Context, _state);
await sc.Context.SendActivityAsync("Got your location!");
return await sc.EndDialogAsync();
}
If you exited the dialog above, and assuming you implemented StateBotAccessors and are using UserProfile property then you retrieve it by:
var _state = await stateBotAccessors.UserState.UserProfileAccessor.GetAsync(context);
Or if you wanted to pass it from a child dialog, you can end with:
return await sc.EndDialogAsync(_state);
Upvotes: 1