How to use State Accessors to get properties in Bot Framework

徘徊边缘 提交于 2020-01-24 21:08:28

问题


One of the functionalities of my bot is handling a Shopping Cart. The user can add items anywhere in the conversation and then finish shopping to close the product cart.

To avoid passing the cart from dialog to dialog I would like to create a UserProfile property in the UserState (The UserProfile property has a ShoppingCart attribute) but I don't quite know how to use this properly.

My Main Dialog contains a set of Child Dialogs and some of them need to be able to access the ShoppingCart object. I found some examples in the samples but none of them do what I want to achieve. In the State Management sample:

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            // Get the state properties from the turn context.

            var conversationStateAccessors =  _conversationState.CreateProperty<ConversationData>(nameof(ConversationData));
            var conversationData = await conversationStateAccessors.GetAsync(turnContext, () => new ConversationData());

            var userStateAccessors = _userState.CreateProperty<UserProfile>(nameof(UserProfile));
            var userProfile = await userStateAccessors.GetAsync(turnContext, () => new UserProfile());

            if (string.IsNullOrEmpty(userProfile.Name))
            {
                // First time around this is set to false, so we will prompt user for name.
                if (conversationData.PromptedUserForName)
                {   
                    // Set the name to what the user provided.
                    userProfile.Name = turnContext.Activity.Text?.Trim();

                    // Acknowledge that we got their name.
                    await turnContext.SendActivityAsync($"Thanks {userProfile.Name}. To see conversation data, type anything.");

                    // Reset the flag to allow the bot to go though the cycle again.
                    conversationData.PromptedUserForName = false;
                }
                else
                {
                    // Prompt the user for their name.
                    await turnContext.SendActivityAsync($"What is your name?");

                    // Set the flag to true, so we don't prompt in the next turn.
                    conversationData.PromptedUserForName = true;
                }
            }

If I understand correctly, every time he wants to get the accessor a new Property is created? Or once a property is created if you call CreateProperty no property will be creater and the accessor is returned?

I thought about getting the accessor on the Bot and then passing it to MainDialogand then to the ChildDialogs but it kinda defeats the purpose of not passing the ShoppingCartthrough Dialogs.

Can't I get the accessors without having to create a Property every time?

I've read this issue which gives a solution to my problem, but then I saw @johnataylor's comment saying

The pattern we follow is to defer the creation of the accessor until we need it - this seems to hide the inherent noise the most effectively.

When should I create the accessors if I want to get the ShoppingCart (which is inside the UserProfile property that I need to access) inside my Dialogs?


回答1:


Fast Answer: You should create the accessor in the all the dialogs where you need to manipulate the state.

Detailed Answer:

CreateProperty does not physically create the property, it just:

Creates a property definition and register it with this BotState

CreateProperty() will return you a BotStatePropertyAccessor from which you can call GetAsync,SetAsync and DeleteAsync those will Get, Set and delete a property from the state cache in the turn context.(Internal cached bot state)

When you call BotState.SaveChangesAsync() this will:

If it has changed, writes to storage the state object that is cached in the current context object for this turn.

Each call of GetAsync,SetAsync will actually call BotState.LoadAsync() first to:

Reads in the current state object and caches it in the context object for this turn.

And when GetAsync() is called and no key is found, it will automatically call SetAsync to set that new property

If you are using AutoSaveStateMiddleware that middleware will:

automatically call .SaveChanges() at the end of the turn for all BotState class it is managing.



来源:https://stackoverflow.com/questions/58338918/how-to-use-state-accessors-to-get-properties-in-bot-framework

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