简体   繁体   中英

Using Bot State Accessors Inside Dialogs

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?

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);

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM