Bot Framework with LUIS - 一个接一个打开表单的问题

Bot Framework with LUIS - Issue with opening Form one after another

我的机器人应该可以帮助删除约会。

  1. 将提示输入用户的 nric(在 RetrieveAppt.cs 中)
  2. 随后,如果我的数据库中有这样的用户,它应该继续提示用户输入he/she要删除的apptId(因为同一个人可能有多个约会)(在DeleteAppt.cs)

问题描述

Exception thrown: 'Microsoft.Bot.Builder.Internals.Fibers.InvalidNeedException' in Microsoft.Bot.Builder.dll

代码示例

RetrieveAppt.cs

using Microsoft.Bot.Builder.FormFlow;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace Bot.Models
{
    [Serializable]
    public class RetrieveAppt
    {
        [Prompt("Please provide your NRIC:")]
        public string Nric { get; set; }

        public override string ToString()
        {
            var builder = new StringBuilder();
            builder.AppendFormat(Nric);
            return builder.ToString();
        }

    }

}

DeleteAppt.cs

using Microsoft.Bot.Builder.FormFlow;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;

namespace Bot.Models
{
    [Serializable]
    public class DeleteAppt
    {
        [Prompt("Please enter the appointment id that you wish to delete/cancel :")]
        public string apptId { get; set; }

        public override string ToString()
        {
            var builder = new StringBuilder();
            builder.AppendFormat(apptId);
            return builder.ToString();
        }
    }
}

ApptLuisDialog.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Bot.Builder.Dialogs;
using Microsoft.Bot.Builder.Luis;
using Microsoft.Bot.Builder.Luis.Models;
using System.Threading.Tasks;
using Microsoft.Bot.Builder.FormFlow;
using Microsoft.Bot.Connector;
using Bot.Models;
using System.Data.SqlClient;
using System.Globalization;

namespace Bot.Dialogs
{
    [LuisModel("I have my own key", "I have my own key")]
    [Serializable]
    class ApptLuisDialog : LuisDialog<ApptLuisDialog>
    {
        String sql = @"Data Source=(localdb)\MSSQLLocalDB; Initial Catalog=Temp.DB; User Id = (insert your username here); Password = (insert your password here); Integrated Security=true;MultipleActiveResultSets = true";

        private static IForm<RetrieveAppt> BuildRetrieveForm()
        {
            var builder = new FormBuilder<RetrieveAppt>();
            return builder.AddRemainingFields().Build();
        }

        private static IForm<DeleteAppt> BuildDeleteForm()
        {
            var builder = new FormBuilder<DeleteAppt>();
            return builder.AddRemainingFields().Build();
        }


        [LuisIntent("")]
        [LuisIntent("None")]
        public async Task None(IDialogContext context, LuisResult result)
        {
            System.Diagnostics.Debug.WriteLine("Entered here: B");
            await context.PostAsync("I'm sorry I don't understand you. However, I can help you to: \n\n" + "1) Retrieve Appointment \n\n" + "2) Create Appointment \n\n" + "3) Delete Appointment \n\n" + "4) Edit Appointment");
            context.Wait(MessageReceived);
        }

        [LuisIntent("RetrieveAppointment")]
        public async Task RetrieveAppointment(IDialogContext context, LuisResult result)
        {
            System.Diagnostics.Debug.WriteLine("Entered here: C");
            var form = new RetrieveAppt();
            var entities = new List<EntityRecommendation>(result.Entities);
            var retrieveAppt = new FormDialog<RetrieveAppt>(form, BuildRetrieveForm, FormOptions.PromptInStart);
            context.Call(retrieveAppt, RetrieveComplete);
        }

        private async Task RetrieveComplete(IDialogContext context, IAwaitable<RetrieveAppt> result)
        {
            RetrieveAppt appt = null;
            try
            {
                appt = await result;
            }
            catch (OperationCanceledException)
            {
                await context.PostAsync("You cancelled the form!");
                return;
            }

            if (appt != null)
            {
                //getting user's input value
                String nric = appt.Nric.ToString();
                List<string> apptInfo = new List<string>();
                //Create connection
                SqlConnection con = new SqlConnection(sql);
                //SQL Command
                SqlCommand cmd = new SqlCommand("SELECT * FROM Appointment a WHERE a.Nric ='" + nric + "'", con);
                //Open sql connection
                con.Open();

                SqlDataReader dr = cmd.ExecuteReader();
                while (dr.Read())
                {
                    String date = dr["AptDate"].ToString();
                    String[] temp = date.Split(null);

                    apptInfo.Add("Appointment ID: " + dr["ApptId"].ToString() + "\n\n"
                        + "Nric: " + dr["Nric"].ToString() + "\n\n"
                        + "Date: " + temp[0] + "\n\n"
                        + "Time: " + dr["AptStartTime"].ToString() + "\n\n"
                        + "Location: " + dr["Location"].ToString() + "\n\n"
                        + "Purpose: " + dr["Purpose"].ToString());
                }

                //Close sql connection
                dr.Close();
                con.Close();
                if (apptInfo.Count == 0)
                {
                    await context.PostAsync("You do not have an appointment/no such NRIC");
                }
                else
                {
                    for (int i = 0; i < apptInfo.Count(); i++)
                    {
                        await context.PostAsync("Your Appointment Info is: " + "\n\n" + apptInfo[i]);
                    }
                }

            }
            else
            {
                await context.PostAsync("Form returned empty response!");
            }

            context.Wait(MessageReceived);
        }

        [LuisIntent("DeleteAppointment")]
        public async Task DeleteAppointment(IDialogContext context, LuisResult result)
        {
            System.Diagnostics.Debug.WriteLine("Entered here: A");
            var form = new RetrieveAppt();
            var retrieveAppt = new FormDialog<RetrieveAppt>(form, BuildRetrieveForm, FormOptions.PromptInStart);
            context.Call(retrieveAppt, Delete);
        }

        private async Task Delete(IDialogContext context, IAwaitable<RetrieveAppt> result)
        {
            RetrieveAppt appt = null;
            try
            {
                appt = await result;
            }
            catch (OperationCanceledException)
            {
                await context.PostAsync("You cancelled the form!");
                return;
            }

            if (appt != null)
            {
                //getting user's input value
                String nric = appt.Nric.ToString().ToUpper();
                List<string> apptInfo = new List<string>();

                //SqlAdapter for inserting new records
                SqlDataAdapter sda = new SqlDataAdapter();

                //Create connection
                SqlConnection con = new SqlConnection(sql);

                //SQL Command to check existing patient
                SqlCommand cmd = new SqlCommand("SELECT * FROM Appointment a WHERE a.Nric ='" + nric + "'", con);

                //Open sql connection
                con.Open();

                SqlDataReader dr = cmd.ExecuteReader();
                while (dr.Read())
                {
                    String date = dr["AptDate"].ToString();
                    String[] temp = date.Split(null);

                    apptInfo.Add("Appointment ID: " + dr["ApptId"].ToString() + "\n\n"
                        + "Nric: " + dr["Nric"].ToString() + "\n\n"
                        + "Date: " + temp[0] + "\n\n"
                        + "Time: " + dr["AptStartTime"].ToString() + "\n\n"
                        + "Location: " + dr["Location"].ToString() + "\n\n"
                        + "Purpose: " + dr["Purpose"].ToString());
                }

                if (apptInfo.Count != 0)
                {
                    **//this is the part that has error, i can't prompt for the appointment id that user wants to delete**
                    System.Diagnostics.Debug.WriteLine("Entered here: AA");
                    var form = new DeleteAppt();
                    var deleteAppt = new FormDialog<DeleteAppt>(form, BuildDeleteForm, FormOptions.PromptInStart);
                    context.Call(deleteAppt, DeleteComplete);

                }
                else
                {
                    //Close sql connection
                    dr.Close();
                    con.Close();
                    await context.PostAsync("Invalid NRIC/No current appointment");
                }
            }
            else
            {
                await context.PostAsync("Form returned empty response!");
            }

            context.Wait(MessageReceived);
        }


    private async Task DeleteComplete(IDialogContext context, IAwaitable<DeleteAppt> result)
    {
        DeleteAppt appt = null;
        try
        {
            appt = await result;
        }
        catch (OperationCanceledException)
        {
            await context.PostAsync("You canceled the form!");
            return;
        }

        if (appt != null)
        {
            //getting user's input value
            String apptId = appt.apptId.ToString();
            List<string> newApptInfo = new List<string>();

            //SqlAdapter for inserting new records
            SqlDataAdapter sda = new SqlDataAdapter();

            //Create connection
            SqlConnection con = new SqlConnection(sql);

            //SQL Command to check existing patient
            String cmd = "DELETE FROM Appointment a WHERE a.ApptId ='" + apptId + "'";

            //Open sql connection
            con.Open();

            try
            {
                sda.InsertCommand = new SqlCommand(cmd, con);
                sda.InsertCommand.ExecuteNonQuery();
                //Close sql connection
                con.Close();
                await context.PostAsync("Appointment " + apptId + " cancelled successfully.");
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Exception caught: " + ex);
            }

        }

        else
        {
            await context.PostAsync("Form returned empty response!");
        }

        context.Wait(MessageReceived);

    }

}

}

预期行为

例如,在bot 提示用户输入NRIC 后,用户输入“123456”。比方说,有 3 个约会与 NRIC“123456”相关联。因此它将首先显示所有 3 个约会(具有以下详细信息:apptId、apptDate、apptTime、locatoin)。

接下来,我希望机器人根据 apptId 提示用户 he/she 想要删除的约会。 (但是这个提示没有显示)

实际结果

Exception thrown: 'Microsoft.Bot.Builder.Internals.Fibers.InvalidNeedException' in Microsoft.Bot.Builder.dll Help needed here definitely

添加 "return" 语句即可解决。 调用 context.Call(deleteAppt, DeleteComplete) 时;不应跟随对 context.Wait(MessageReceived) 的调用。所以在 context.Call(deleteAppt, DeleteComplete);

之后添加一条 return 语句
if (apptInfo.Count != 0)
{
    //this is the part that has error, i can't prompt for the appointment id that user wants to delete
    System.Diagnostics.Debug.WriteLine("Entered here: AA");
    var form = new DeleteAppt();
    var deleteAppt = new FormDialog<DeleteAppt>(form, BuildDeleteForm, FormOptions.PromptInStart);
    context.Call(deleteAppt, DeleteComplete);
    return;
}