The AutoSaveStateMiddleware is a great addition to a Bot project. On every turn it can save the BotState objects you configure it to look after. Takes a way a lot of noise and accidentally forgetting to call save yourself. However, this is a little gotcha with this mechanism. Consider a classic Pizza ordering scenario;
- USER – What pizzas do you have?
- BOT – We have Vegetarian, Pepperoni, Ham & Cheese, Ham & Mushroom
From a Bot Framework perspective you might implement that as a series of Waterfall steps – potentially using a 3rd party service, i.e. potentially unreliable – slow;
- Gather Available Ingredients For Location Step, NextAsync
- Gather Available Pizza Recipes for Available Ingredients Step, NextAsync
- Show Available Pizza Step
So far so good. Now, let’s say that before we started this Waterfall off we provided the user with a ‘See our drinks menu’ hero card button. What we expect to happen is that the drinks button will be shown before the potentially longer running food menu is processed and shown to the user. But what happens if the user presses that drinks button before the food menu has run through its steps and displayed its results? Well, something not so pleasant is the answer. Since the AutoSaveState will fire on the Turn, it won’t have fired until it prompts the user for their pizza choice. Therefore, even though the Drink button is in the same overall flow, the same activity, when the message is received by the bot it will NOT have an Active Dialog set in its context. This means that the message will NOT be passed onto what we think is our active dialog.
What solutions are there?
- You can manually call the Save State, but it’s sort of a pain because that’s why we’re using the AutoSaveState
- Simply ignore this issue. You may lose messages but the user will just try again
- Use a base class for your dialogs that overrides NextAsync and saves the Dialog state