Bot Framework с LUIS - проблема с открытием формы один за другим

Мой бот должен помочь удалить встречу.

  1. Будет отображено приглашение для пользователя nric (в RetrieveAppt.cs).
  2. Впоследствии, если в моей базе данных есть такой пользователь, он должен предложить пользователю ввести apptId, который он / она хочет удалить (так как может быть несколько назначений, сделанных одним и тем же человеком) (в DeleteAppt.cs)

Описание проблемы

Возникло исключение: "Microsoft.Bot.Builder.Internals.Fibers.InvalidNeedException" в 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);

    }

}

}

Ожидаемое поведение

Например, после того, как бот предложит пользователю ввести NRIC, пользователь вводит "123456". Допустим, есть 3 назначения, связанные с NRIC "123456". Таким образом, сначала будут показаны все 3 встречи (со следующими деталями: apptId, apptDate, apptTime, locatoin).

Далее я хочу, чтобы бот попросил пользователя назначить встречу, которую он / она хочет удалить из базы по apptId. (Но эта подсказка не отображается)

Фактические результаты

Исключение: "Microsoft.Bot.Builder.Internals.Fibers.InvalidNeedException" в Microsoft.Bot.Builder.dll.

1 ответ

Решение

Добавление "возврата" решило бы это. При совершении вызова context.Call(deleteAppt, DeleteComplete); не должно следовать вызову context.Wait(MessageReceived). Поэтому добавьте оператор возврата после 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;
}
Другие вопросы по тегам