question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Adaptive Dialogs: Using a ChoiceInput inside a Foreach creates an infinite loop

See original GitHub issue

Version

4.14.1

Describe the bug

When creating an AdaptiveDialog which has a Foreach action containing a ChoiceInput, we have found that the Foreach never gets beyond the second item in the array and loops infinitely

To Reproduce

Example dialog:

var searchStart = new AdaptiveDialog("searchStart") { Generator = new TemplateEngineLanguageGenerator() };
	var choices = new ChoiceSet();
	choices.Add(new Choice("Red"));
	choices.Add(new Choice("Green"));
	choices.Add(new Choice("Blue"));

	searchStart.Triggers.Add(new OnBeginDialog
            {
                Actions =
                {
                    new CodeAction(async (dc,obj) =>
                    {
                        dc.State.SetValue("dialog.myArray", new string[]{"one", "two", "three"});
                        return await dc.EndDialogAsync();
                    }),
                    new Foreach()
                    {
                        ItemsProperty="dialog.myArray",
                        Actions = new List<Dialog>
                        {
                            new CodeAction(async (dc, obj) =>
                            {
                                var loopIndex = dc.State.GetValue<int>("$foreach.index");
                                await dc.Context.SendActivityAsync($"current index: {loopIndex}");
                                return await dc.EndDialogAsync();
                            }),
                            new ChoiceInput
                            {
                                Id = "mychoice",
                                DefaultValue = "${turn.Activity.Text}",
                                Property = "dialog.myColour",
                                Prompt = new ActivityTemplate("What is your favourite colour?"),
                                Choices = choices,
                                AlwaysPrompt = true,


                            },
                            new CodeAction(async (dc, obj) =>
                            {
                                var yourColour = dc.State.GetValue<object>("dialog.myColour");
                                await dc.Context.SendActivityAsync("You said:" + yourColour.ToString());

                                return await dc.EndDialogAsync();
                            })


                        }

                    },
                    new SendActivity("All done")
                }
            });

Result: image

Additional context

We have been looking at the code and this appears to be becuase the Foreach class uses a local private ‘index’ int field to store the position in the array. This confused us as it was already being stored in state in ‘dialog.foreach.index’. What appears to be happening is that the Foreach is being re-instantiated for some reason and so index is being reset to 0 every time.

One option could be that you remove the local private index property and reuse ‘dialog.foreach.index’. We’d suggest changing NextItemAsync to be

protected virtual async Task<DialogTurnResult> NextItemAsync(DialogContext dc, CancellationToken cancellationToken = default)
        {
            // Get list information
            var result = dc.State.GetValue<object>(this.ItemsProperty.GetValue(dc.State));
            var list = ConvertToList(result);

            var index = dc.State.GetValue<int>( Index.GetValue(dc.State), () => -1);

            // Next item
            if (list != null && ++index < list.Count)
            {
                // Persist index and value
                dc.State.SetValue(Value.GetValue(dc.State), list[index][IterationValue]);
                dc.State.SetValue(Index.GetValue(dc.State), list[index][IterationKey]);

                // Start loop
                return await this.BeginActionAsync(dc, 0, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                // End of list has been reached, or the list is null
                return await dc.EndDialogAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
            }
        }

and put this in BeginDialogAsync

dc.State.RemoveValue(Index.GetValue(dc.State));

Unless someone can provide another solution to us?

Thanks in advanced

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

1reaction
Danieladucommented, Aug 19, 2021

hi @j-melville Sorry that I could not reproduce the issue, but this problem does exist, while the action is broken and would be restored from some other “store” or “cache”, the value of the private value would lose its previous value. And, your fix solution is right! I just get a pr to fix this.

Additional thing, your adaptive card could be simplified to this:

var searchStart = new AdaptiveDialog("searchStart") { Generator = new TemplateEngineLanguageGenerator() };
var choices = new ChoiceSet();
choices.Add(new Choice("Red"));
choices.Add(new Choice("Green"));
choices.Add(new Choice("Blue"));

searchStart .Triggers.Add(new OnBeginDialog
{
    Actions =
    {
        new SetProperty()
        {
            Property = "dialog.myArray",
            Value = "=['one', 'two', 'three']"
        },
        new Foreach()
        {
            ItemsProperty = "dialog.myArray",
            Actions = new List<Dialog>
            {
                new SendActivity("current index: ${$foreach.index}"),
                new ChoiceInput
                {
                    Id = "mychoice",
                    DefaultValue = "${turn.Activity.Text}",
                    Property = "dialog.myColour",
                    Prompt = new ActivityTemplate("What is your favourite colour?"),
                    Choices = choices,
                    AlwaysPrompt = true
                },
                new SendActivity("You said:${string(dialog.myColour)}"),
            },
        },
        new SendActivity("All done")
    }
});

The result: image

Hope it helpful. Thanks!

0reactions
j-melvillecommented, Aug 19, 2021

Ahh that sounds like a good idea, thanks.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Infinite loop in the bot when using adaptive dialogs and ...
Version 4.12.3 Describe the bug A customer reported: When using adaptive dialogs with a custom recognizer, the bot remained stuck in a loop....
Read more >
Prebuilt actions for adaptive dialogs - Bot Service
Actions in adaptive dialogs - reference guide ... ForEach, Loop: For each item, Runs a set of actions on each item in a...
Read more >
Why does a foreach loop in infinite loop go on forever?
I'm trying to implement a recipe manager where the user can choose how they sort each recipe. Based on the user's input, I...
Read more >
Infinite Loop in foreachloop - Audio
Hey guys,. I'm having problems with an infinite loop being detected in my foreachloop, which I just can't understand how it becomes infinite....
Read more >
how to stop infinite loop
Hello,. I have accidentally created an infinite loop using if else condition, and now it is endless creating new data points.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found