【问题标题】:How to use State Accessors to get properties in Bot Framework如何使用状态访问器获取 Bot Framework 中的属性
【发布时间】:2019-10-11 10:04:35
【问题描述】:

我的机器人的功能之一是处理购物车。用户可以在对话中的任意位置添加商品,然后完成购物以关闭产品购物车。

为了避免将购物车从一个对话框传递到另一个对话框,我想在UserState 中创建一个UserProfile 属性(UserProfile 属性有一个ShoppingCart 属性)但我不太清楚如何使用这是正确的。

我的主对话框包含一组子对话框,其中一些需要能够访问ShoppingCart 对象。我在示例中找到了一些示例,但它们都没有达到我想要的效果。在状态管理示例中:

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

如果我理解正确,每次他想要获取访问器时都会创建一个新属性?或者,如果您调用CreateProperty,一旦创建了一个属性,就不会创建任何属性并返回访问器?

我曾考虑在 Bot 上获取访问器,然后将其传递给 MainDialog,然后传递给 ChildDialogs,但这有点违背了不通过对话框传递 ShoppingCart 的目的。

我不能在每次都创建一个属性的情况下获取访问器吗?

我已阅读 this issue,它为我的问题提供了解决方案,但后来我看到 @johnataylor 的评论说

我们遵循的模式是将访问器的创建推迟到我们需要它时——这似乎最有效地隐藏了固有的噪音。

如果我想在我的对话框中获取ShoppingCart(在我需要访问的UserProfile 属性中),我应该何时创建访问器?

【问题讨论】:

    标签: c# asp.net-core botframework accessor


    【解决方案1】:

    快速回答: 您应该在需要操作状态的所有对话框中创建访问器。

    详细回答:

    CreateProperty 不会实际创建属性,它只是:

    创建一个属性定义并将其注册到此 BotState

    CreateProperty() 将返回一个 BotStatePropertyAccessor,您可以从中调用 GetAsyncSetAsync >DeleteAsync 将在回合上下文中从状态缓存中获取、设置和删除属性。(内部缓存机器人状态)

    当您调用 BotState.SaveChangesAsync() 时,这将:

    如果发生了变化,则将本回合缓存在当前上下文对象中的状态对象写入存储。

    GetAsyncSetAsync 的每次调用实际上都会首先调用 BotState.LoadAsync() 以:

    读入当前状态对象并将其缓存在上下文中 本回合的对象。

    GetAsync() 被调用并且 没有找到键 时,它会自动调用 SetAsync 来设置新的属性

    如果您使用 AutoSaveStateMiddleware,该中间件将:

    在回合结束时为所有人自动调用 .SaveChanges() 它正在管理的 BotState 类。

    【讨论】:

    • 这是我多年来一直在寻找的答案,现在我明白了。所以为了澄清,我应该在每个需要它的对话框中CreateProperty 以获得访问器。如果我每次都调用它也没关系,因为除非我SetAsync,否则财产不会受到影响,对吗?最后一件事,我应该使用 CreateProperty 在 Dialog 构造函数中初始化访问器,还是在需要时将其存储在局部变量中?
    • 是的,您需要在每个需要的对话框中CreateProperty,以便您可以获取访问器,如果您每次都调用它并不重要,因为只有在实际存在时才会发生存储写入改变。我所做的是在构造函数中注入 UserState 并将构造函数中的访问器初始化为私有字段private IStatePropertyAccessor&lt;UserProfile &gt; _stateAccessor;。这样,例如,如果您有一个瀑布对话框,则可以在每个步骤中使用相同的访问器
    • 我在 Startup 中有 UserState 作为单例。我可以将它 DI 到 MainDialog,但是它必须作为参数传递给子对话框,对吗?因为在MainDialogAddDialog(new ShoppingDialog(...)) 时必须调用构造函数。
    • 如果您在 Startup 中将对话框添加为瞬态,例如:services.AddTransient&lt;ShoppingDialog&gt;(); 在您的 MainDialog 中,您只需要 AddDialog(shoppingDialog);其中 shoppingDialog 作为参数传递给您的主构造函数,然后在您的 ShoppingDialog 中注入 UserState
    • 最后一个问题,为什么我应该将用户和对话状态添加为瞬态?不应该将它们添加为Singleton 对于我们想要做的事情更准确吗?将状态添加为Transient 有什么好处?
    猜你喜欢
    • 2019-10-16
    • 1970-01-01
    • 2021-10-02
    • 2021-09-23
    • 1970-01-01
    • 2015-07-07
    • 1970-01-01
    • 1970-01-01
    • 2019-04-14
    相关资源
    最近更新 更多