[英]Adaptive card id collision when using same card for second time
I have a dialog (bot framework dialog) which works great the first time user enters it.我有一个对话框(机器人框架对话框),它在用户第一次输入时效果很好。 Dialog displays user with an adaptive card containing Input.ChoiceSet which has a id property set to "substitution".
对话框向用户显示包含 Input.ChoiceSet 的自适应卡片,该卡片的 id 属性设置为“替换”。 When user enters the same dialog for the second time ChoiseSet gets displayed properly but on Action.Submit I get an id collision for id "substitution".
当用户第二次进入同一个对话框时,ChoiseSet 正确显示,但在 Action.Submit 上,我收到了 id“替换”的 id 冲突。
My issue is similar to issue of this user: https://github.com/microsoft/AdaptiveCards/issues/3225#issuecomment-710626684 .我的问题类似于该用户的问题: https : //github.com/microsoft/AdaptiveCards/issues/3225#issuecomment-710626684 。 But his solution to the problem has no effect for my case.
但是他对问题的解决方案对我的情况没有影响。 I keep getting the same error.
我不断收到同样的错误。
I get that id should be unique but I shouldn't be forced to dynamically set id of the same ChoiceSet card.我知道 id 应该是唯一的,但我不应该被迫动态设置同一个 ChoiceSet 卡的 id。
My adaptive card:我的自适应卡:
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.3",
"body": [
{
"type": "TextBlock",
"text": "<to-be-set-with-code>"
},
{
"type": "Input.ChoiceSet",
"id": "substitution",
"style": "compact",
"isMultiSelect": false,
"value": "1",
"choices": []
}
],
"actions": [
{
"type": "Action.Submit",
"title": "OK",
"data": {
"msteams": {
"type": "messageBack",
"text": "back"
}
}
}
]
}
My code (inside dialog step):我的代码(在对话步骤内):
private async Task<DialogTurnResult> SubstitutionStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var adaptiveCardJson = await File.ReadAllTextAsync(Cards.TextWithChoiceSetCard, cancellationToken);
var card = AdaptiveCard.FromJson(adaptiveCardJson).Card;
var textBlock = card.Body[0] as AdaptiveTextBlock;
textBlock.Text = "Who will be your substitution?";
// get current teams user info
var userStateAccessors = UserState.CreateProperty<UserProfile>(nameof(UserProfile));
var userProfile = await userStateAccessors.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken);
// generate choices
var adaptiveChoices = new List<AdaptiveChoice>();
foreach (var user in ApiWrapper.RetrieveInstanceUsers())
{
// do not include current user and bot user
if (user.UserName == ClientData.Username || string.Equals(user.Email, userProfile.UserPrincipalName, StringComparison.CurrentCultureIgnoreCase)) continue;
adaptiveChoices.Add(new AdaptiveChoice
{
Title = $"{user.UserName} ({user.Email})",
Value = user.ToJson()
});
}
var choiceSet = card.Body[1] as AdaptiveChoiceSetInput;
choiceSet.Choices = adaptiveChoices;
var attachment = new Attachment
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = card
};
var reply = stepContext.Context.Activity.CreateReply();
reply.Attachments = new List<Attachment> {attachment};
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions {Prompt = reply}, cancellationToken);
}
I found a workaround to fix the exception:我找到了解决异常的解决方法:
AdaptiveCards: Collision detected for id 'cardFieldId' . AdaptiveCards:检测到 ID 'cardFieldId' 的冲突。
Let's say we have these WaterfallSteps:假设我们有这些 WaterfallSteps:
And let's say that we have this card:假设我们有这张卡:
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.3",
"body":
[
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": 2,
"items": [
{
"type": "TextBlock",
"wrap": true,
"text": "Select an assistance."
},
{
"type": "Input.ChoiceSet",
"choices": [],
"placeholder": "---",
"id": "assistanceId"
}
]
}
]
},
{
"type": "ColumnSet",
"columns": [
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "ActionSet",
"actions": [
{
"type": "Action.Submit",
"title": "CANCEL",
"id": "bnCancel"
}
]
}
]
},
{
"type": "Column",
"width": "stretch",
"items": [
{
"type": "ActionSet",
"actions": [
{
"type": "Action.Submit",
"title": "CONFIRM",
"style": "positive",
"id": "bnConfirm"
}
]
}
]
}
]
}
]
}
The source code of steps:步骤源码:
private async Task<DialogTurnResult> InizialStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var message = "Insert YES or NO.";
var promptMessage = MessageFactory.Text(message, message, InputHints.ExpectingInput);
return await stepContext.PromptAsync(nameof(TextPrompt), new PromptOptions { Prompt = promptMessage }, cancellationToken);
}
private async Task<DialogTurnResult> CardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var actionTypes = (List<ActionTypeFullItem>)stepContext.Options;
var txt = (string)stepContext.Result;
if (txt.Equals("YES", StringComparison.OrdinalIgnoreCase))
{
// If user say 'YES', show card.
// Retrieve JSON of our card from EmbeddedResource
var jCard = await Utils.GetCardJsonAsync("assistanceType");
// Workaround BUG: https://stackoverflow.com/questions/69358336/adaptive-card-id-collision-when-using-same-card-for-second-time
// Replace 'FIELDID' with 'FIELDID_RANDOMGUID'.
var chooseFieldId = $"assistanceId_{Guid.NewGuid()}";
var bnCancelFieldId = $"bnCancel_{Guid.NewGuid()}";
var bnConfirmFieldId = $"bnConfirm_{Guid.NewGuid()}";
jCard = jCard.Replace("assistanceId", chooseFieldId);
jCard = jCard.Replace("bnCancel", bnCancelFieldId);
jCard = jCard.Replace("bnConfirm", bnConfirmFieldId);
// Create the Adaptive Card from JSON
var card = AdaptiveCard.FromJson(jCard).Card;
// Popolate our choose field (extensions method created by me)
var chooses = new List<AdaptiveChoice>();
foreach (var actionType in actionTypes)
{
chooses.Add(new AdaptiveChoice
{
Title = actionType.Title,
Value = actionType.Id.ToString()
});
}
card.SetChoiseField(chooseFieldId, chooses);
// Popolate cancel button submit data (extensions method created by me)
// We need 'assistanceFieldId' to retrieve the value of choose filed in the next step.
card.AddSubmitData(bnCancelFieldId, new Dictionary<string, object>
{
{ "confirm", false },
{ "assistanceFieldId", chooseFieldId }
});
// Popolate confirm button submit data (extensions method created by me)
// We need 'assistanceFieldId' to retrieve the value of choose filed in the next step.
card.AddSubmitData(bnConfirmFieldId, new Dictionary<string, object>
{
{ "confirm", true },
{ "assistanceFieldId", chooseFieldId }
});
// Prompt card
return await stepContext.PromptCardAsync(card, cancellationToken);
}
return await stepContext.NextAsync(new { confirm = false }, cancellationToken);
}
private async Task<DialogTurnResult> FinalStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var actionTypes = (List<ActionTypeFullItem>)stepContext.Options;
var selectedActionType = default(ActionTypeFullItem);
// Example JSON result
// { "confirm":true, "assistanceFieldId": "assistanceId_d68fbc77-d96e-4d1c-b3eb-4d4c31ccf2af", "assistanceId_d68fbc77-d96e-4d1c-b3eb-4d4c31ccf2af":"19" }
var jObject = Newtonsoft.Json.Linq.JObject.Parse((string)stepContext.Result);
if (jObject.ContainsKey("confirm") && !(bool)jObject["confirm"])
{
// User has clicked the 'cancel' button -> go back to the first step.
return await stepContext.ReplaceDialogAsync(InitialDialogId, actionTypes, cancellationToken);
}
var assistanceFieldId = (string)jObject["assistanceFieldId"];
if (selectedActionType == null)
{
selectedActionType = actionTypes.SingleOrDefault(a => a.Id.ToString().Equals((string)jObject[assistanceFieldId], StringComparison.OrdinalIgnoreCase));
}
return await stepContext.EndDialogAsync(selectedActionType, cancellationToken);
}
The idea is very simple, instead of always keeping the same ID for each field, we add a random guid to always have a different ID.这个想法很简单,我们不是总是为每个字段保持相同的 ID,而是添加一个随机的 guid 以始终具有不同的 ID。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.