Using Bot State Accessors Inside Dialogs

自古美人都是妖i 提交于 2020-02-03 10:24:46

问题


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?


回答1:


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



回答2:


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


来源:https://stackoverflow.com/questions/53778670/using-bot-state-accessors-inside-dialogs

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!