【问题标题】:Multiple messages to the bot in quick succession crash it快速连续向机器人发送多条消息使其崩溃
【发布时间】:2017-09-07 00:06:44
【问题描述】:

设置

我有一个在 .NET + Bot Framework + Azure + Facebook Messenger 上运行的机器人。

最初的问题

我试图解决一个问题,当向机器人发送多条消息时触发异常和 HTTP 错误 412。Microsoft 在这里描述了这个问题:https://docs.microsoft.com/en-us/bot-framework/troubleshoot-general-problems#what-causes-an-error-with-http-status-code-412-precondition-failed-or-http-status-code-409-conflict

第一个解决方案

在上面的页面中,Microsoft 提供了一个过时的示例代码来解决此问题。在this github issue 中,有一个应该可以工作的代码的修订版本。我把它放在我的 MessageController 的构造函数中:

    static MessagesController()
    {
        // Prevent exception in the bot and HTTP error 412 when the user
        // sends multiple messages in quick succession. This may cause 
        // potential problems with consistency of getting/setting user
        // properties. 
        // See https://docs.microsoft.com/en-us/bot-framework/troubleshoot-general-problems#what-causes-an-error-with-http-status-code-412-precondition-failed-or-http-status-code-409-conflict
        // for details. The above link contains wrong code sample, revised
        // code is from here: https://github.com/Microsoft/BotBuilder/issues/2345
        var builder = new ContainerBuilder();
        builder
            .Register(c => new CachingBotDataStore(c.ResolveKeyed<IBotDataStore<BotData>>(typeof(ConnectorStore)), CachingBotDataStoreConsistencyPolicy.LastWriteWins))
            .As<IBotDataStore<BotData>>()
            .AsSelf()
            .InstancePerLifetimeScope();
        builder.Update(Conversation.Container);
    } 

第二个问题

现在,当我快速连续向机器人发送多条消息时,仍然会发生异常。但是,它从 HTTP 错误 412 更改为其他内容:

发生了一个或多个错误。在 System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions) 在 System.Threading.Tasks.Task1.GetResultCore(Boolean waitCompletionNotification) 在 System.Threading.Tasks.Task1.get_Result() 在 MyBot.SetUserDataProperty(Activity 活动,字符串 PropertyName , String ValueToSet) 在 C:\Users\xxx.cs:line 230

更新:我检查了上面的InnerException,结果是同样的旧HTTP错误412:

远程服务器返回错误:(412) Precondition Failed.

违规代码是写入机器人存储的函数。上面引用的第 230 行是这个函数的最后一行:

    public static void SetUserDataProperty(Activity activity, string PropertyName, string ValueToSet)
    {
        StateClient client = activity.GetStateClient();
        BotData userData = client.BotState.GetUserData(activity.ChannelId, activity.From.Id);
        userData.SetProperty<string>(PropertyName, ValueToSet);

        //client.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData);
        // Await async call without making the function asynchronous:
        var temp = Task.Run(() => client.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData)).Result;
    }

问题

我还能做些什么来确保用户能够快速连续发送多条消息而不会在写入 BotState 存储时触发异常?

【问题讨论】:

  • 请发布代码以重现问题
  • 你使用的是默认状态客户端吗?
  • @JasonSowers 不确定。我用它来将键值对保存到机器人状态:context.UserData.SetValue&lt;string&gt;(PropertyName, ValueToSet);
  • 还有一个问题,你是在对话框中调用这个方法SetUserDataProperty吗?

标签: c# bots botframework facebook-messenger-bot


【解决方案1】:

我觉得这里有几个问题

您尝试执行此操作的方式activity.GetStateClient(); 仅用于原型设计。我们不建议将此方法用于生产级代码。您可以在对话框中设置context.UserData.SetValue("food", "Nachos" ); 之类的用户数据,当对话框序列化时,这些值将自动保存。

您很可能是从对话框中调用此方法SetUserDataProperty,所以当您这样做var temp = Task.Run(() =&gt; client.BotState.SetUserDataAsync(activity.ChannelId, activity.From.Id, userData)).Result; 时,它会发生冲突并导致错误。

请查看此blog post 以了解更多信息

以下是如何实施您的后续问题:

        if (activity.Type == ActivityTypes.Message)
        {

            var message = activity as IMessageActivity;
            using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
            {
                var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
                var key = new AddressKey()
                {
                    BotId = message.Recipient.Id,
                    ChannelId = message.ChannelId,
                    UserId = message.From.Id,
                    ConversationId = message.Conversation.Id,
                    ServiceUrl = message.ServiceUrl
                };
                ConversationReference r = new ConversationReference();
                var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None);

                userData.SetProperty("key 1", "value1");
                userData.SetProperty("key 2", "value2");

                await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None);
                await botDataStore.FlushAsync(key, CancellationToken.None);
            }
            await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
        }

你需要实现这个类或类似的东西:

public class AddressKey : IAddress
{
    public string BotId { get; set; }
    public string ChannelId { get; set; }
    public string ConversationId { get; set; }
    public string ServiceUrl { get; set; }
    public string UserId { get; set; }
}

【讨论】:

  • 谢谢!但是,当IDialogContext 尚不可用时,是否有推荐的方法从MessageController 中设置用户数据?
  • 是的,我会编辑我的答案给你看。希望这会有所帮助,祝你好运!
猜你喜欢
  • 2021-02-20
  • 1970-01-01
  • 1970-01-01
  • 2019-09-12
  • 2022-11-02
  • 2012-09-21
  • 1970-01-01
  • 2018-05-17
  • 1970-01-01
相关资源
最近更新 更多