Bot Framework с LUIS - проблема с открытием формы один за другим
Мой бот должен помочь удалить встречу.
- Будет отображено приглашение для пользователя nric (в RetrieveAppt.cs).
- Впоследствии, если в моей базе данных есть такой пользователь, он должен предложить пользователю ввести 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;
}