private List AssignEntities()

in libraries/Microsoft.Bot.Builder.Dialogs.Adaptive/AdaptiveDialog.cs [1562:1684]


        private List<EntityInfo> AssignEntities(ActionContext actionContext, Dictionary<string, List<EntityInfo>> entities, EntityAssignments existing, string lastEvent)
        {
            var assignments = new EntityAssignments();
            if (!actionContext.State.TryGetValue<string[]>(DialogPath.ExpectedProperties, out var expected))
            {
                expected = Array.Empty<string>();
            }

            // default op from the last Ask action.
            var askDefaultOp = actionContext.State.GetValue<JObject>(DialogPath.DefaultOperation);

            // default operation from the current adaptive dialog.
            var defaultOp = dialogSchema.Schema[DefaultOperationKey]?.ToObject<JObject>();

            var nextAssignment = existing.NextAssignment();
            var candidates = (from candidate in RemoveOverlappingPerProperty(Candidates(entities, expected, lastEvent, nextAssignment, askDefaultOp, defaultOp))
                              orderby
                                candidate.IsExpected descending,
                                candidate.Operation == DefaultOperation(candidate, askDefaultOp, defaultOp) descending
                              select candidate).ToList();
            var usedEntities = new HashSet<EntityInfo>(from candidate in candidates select candidate.Value);
            List<string> expectedChoices = null;
            var choices = new List<EntityAssignment>();
            while (candidates.Any())
            {
                var candidate = candidates.First();

                // Alternatives are either for the same entity or from different roots
                var alternatives = (from alt in candidates
                                    where candidate.Value.Overlaps(alt.Value) && (!candidate.Value.SharesRoot(alt.Value) || candidate.Value == alt.Value)
                                    select alt).ToList();
                candidates = candidates.Except(alternatives).ToList();
                foreach (var alternative in alternatives)
                {
                    usedEntities.Add(alternative.Value);
                }

                if (candidate.IsExpected && candidate.Value.Name != UtteranceKey)
                {
                    // If expected binds entity, drop unexpected alternatives unless they have an explicit operation
                    alternatives.RemoveAll(a => !a.IsExpected && a.Value.Operation == null);
                }

                // Find alternative that covers the largest amount of utterance
                candidate = (from alternative in alternatives orderby alternative.Value.Name == UtteranceKey ? 0 : alternative.Value.End - alternative.Value.Start descending select alternative).First();

                // Remove all alternatives that are fully contained in largest
                alternatives.RemoveAll(a => candidate.Value.Covers(a.Value));

                var mapped = false;
                if (candidate.Operation == AdaptiveEvents.ChooseEntity)
                {
                    // Property has resolution so remove entity ambiguity
                    var entityChoices = existing.Dequeue(actionContext);
                    candidate.Operation = entityChoices.Operation;
                    if (candidate.Value.Value is JArray values && values.Count > 1)
                    {
                        // Resolve ambiguous response to one of the original choices
                        var originalChoices = entityChoices.Value.Value as JArray;
                        var intersection = values.Intersect(originalChoices);
                        if (intersection.Any())
                        {
                            candidate.Value.Value = intersection;
                        }
                    }
                }
                else if (candidate.Operation == AdaptiveEvents.ChooseProperty)
                {
                    choices = nextAssignment.Alternatives.ToList();
                    var choice = choices.Find(a => MatchesAssignment(candidate.Value, a));
                    if (choice != null)
                    {
                        // Resolve choice, pretend it was expected and add to assignments
                        expectedChoices = new List<string>();
                        choice.IsExpected = true;
                        choice.Alternative = null;
                        if (choice.Property != null)
                        {
                            expectedChoices.Add(choice.Property);
                        }
                        else if (choice.ExpectedProperties != null)
                        {
                            expectedChoices.AddRange(choice.ExpectedProperties);
                        }

                        AddAssignment(choice, assignments);
                        choices.RemoveAll(c => c.Value.Overlaps(choice.Value));
                        mapped = true;
                    }
                }

                candidate.AddAlternatives(alternatives);
                if (!mapped)
                {
                    AddAssignment(candidate, assignments);
                }
            }

            if (expectedChoices != null)
            {
                // When choosing between property assignments, make the assignments be expected.
                if (expectedChoices.Any())
                {
                    actionContext.State.SetValue(DialogPath.ExpectedProperties, expectedChoices);
                }

                // Add back in any non-overlapping choices that have not been resolved
                while (choices.Any())
                {
                    var choice = choices.First();
                    var overlaps = from alt in choices where choice.Value.Overlaps(alt.Value) select alt;
                    choice.AddAlternatives(overlaps);
                    AddAssignment(choice, assignments);
                    choices.RemoveAll(c => c.Value.Overlaps(choice.Value));
                }

                existing.Dequeue(actionContext);
            }

            var operations = new EntityAssignmentComparer(dialogSchema.Schema[OperationsKey]?.ToObject<string[]>() ?? Array.Empty<string>());
            MergeAssignments(assignments, existing, operations);
            return usedEntities.ToList();
        }