Fabian
Fabian

Reputation: 129

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

My bot is supposed to help delete appointment.

  1. A prompt for user's nric will be done (in RetrieveAppt.cs)
  2. Subsequently, if there is such user in my database, it should go on to prompt user to enter the apptId which he/she wants to delete (as there may be multiple appointments made by same person) (in DeleteAppt.cs)

Issue Description

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

Code Example

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);

    }

}

}

Expected Behavior

For example, after bot prompts user to input NRIC, user inputs "123456". So let's say, there are 3 appointments linked to NRIC "123456". So it will show all 3 appointments (with the following details: apptId, apptDate, apptTime, locatoin) first.

Next, I want the bot to prompt the user for the appointment that he/she wants to delete base on the apptId. (But this prompt is not showing)

Actual Results

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

Upvotes: 0

Views: 71

Answers (1)

Fabian
Fabian

Reputation: 129

adding a "return" statement would solve it. When making the call to context.Call(deleteAppt, DeleteComplete); there should not follow a call to context.Wait(MessageReceived). So add a return statement after context.Call(deleteAppt, DeleteComplete);

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;
}

Upvotes: 0

Related Questions