IBotDataStore.FlushAsync Exception: The data is changed

前言

最新有個需求就是要判斷目前 User 是不是在進行某項作業, 如果不是就推送包含 Button 的訊息給 User, 不然就推文字通知訊息等目前的作業完成後,再顯示待辦的 Button 訊息給 User .

研究

最簡單的就是將這個 flag 寫在 BotState 之中,所以我們可以在使用者加入時,將它的 Conversation 記錄下來,如下,

if (message.Type == ActivityTypes.ConversationUpdate)
{
Func<ChannelAccount, bool> isChatbot =
channelAcct => channelAcct.Id == message.Recipient.Id;
if (message.MembersAdded.Any(isChatbot))
{
var memberAdded = message.MembersAdded.FirstOrDefault();
//這裡請自行處理將該 user 的 Conversation 儲存起來,為了方便,這裡將它存在一個變數之中
WebApiApplication._ConversationReference = message.ToConversationReference();
}
}

然後在申請作業記錄這個 Flag ,當然,結束時請記得將這個 Flag 清除哦!

using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, context.Activity.AsMessageActivity()))
{
var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
var key = Address.FromActivity(context.Activity);
var botUserData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, default(CancellationToken));
botUserData.SetProperty<string>(“dialog”, “RootDialog:MessageReceivedAsync”);
await botDataStore.SaveAsync(key, BotStoreType.BotUserData, botUserData, default(CancellationToken));
await botDataStore.FlushAsync(key, default(CancellationToken));
}

可是執行時,卻發生了「Exception: The data is changed」的錯誤,如下圖,
[Exception: The data is changed]

因為 botframework 會自動去幫我們將 State 存檔,所以當它存檔時,發現狀態被改掉了,所以就出現「Exception: The data is changed」的錯誤 .

嗯, 那怎麼辦呢? 那就調整程式, 不要去呼叫 FlushAsync, 讓 botframework 去存就可以了, 所以程式調整如下,

using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, context.Activity.AsMessageActivity()))
{
var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
var key = Address.FromActivity(context.Activity);
var botUserData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, default(CancellationToken));
botUserData.SetProperty<string>(“dialog”, “RootDialog:MessageReceivedAsync”);
await botDataStore.SaveAsync(key, BotStoreType.BotUserData, botUserData, default(CancellationToken));
//await botDataStore.FlushAsync(key, default(CancellationToken));
}

所以我們的通知程式就可以透過 ConversationReference 轉回 Activity, 取回 BotState, 例如,我新增一個 api method 來顯示 BotState , 如下,

[Route(“api/Messages/GetDialogData”)]
[HttpGet]
public async Task<HttpResponseMessage> GetDialogData()
{
var resp = new HttpResponseMessage(HttpStatusCode.OK);
if (WebApiApplication._ConversationReference != null)
{
var message = WebApiApplication._ConversationReference.GetPostToBotMessage();
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message.AsMessageActivity()))
{
var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
var key = Address.FromActivity(message);
var botUserData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, default(CancellationToken));
var dialogData = botUserData.GetProperty<string>(“dialog”);
resp.Content = new StringContent($”<html><body>Dialogs:{dialogData}</body></html>”, System.Text.Encoding.UTF8, @”text/html”);
}
}
return resp;
}

所以在 Dialog 中儲存 State 時 , 可以注意一下這種狀況哦 ^_^

Demo 專案可以參考 rainmakerho/Exception_DataChanged

參考資料

Microsoft Bot Framework: Exception: The data is changed

作者: 亂馬客

亂馬客 @叡揚資訊 rainmaker_ho@gss.com.tw https://rainmakerho.github.io/ https://www.slideshare.net/rainmakerho

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *