Bot Framework with LUIS - 一个接一个打开表单的问题
Bot Framework with LUIS - Issue with opening Form one after another
我的机器人应该可以帮助删除约会。
- 将提示输入用户的 nric(在 RetrieveAppt.cs 中)
- 随后,如果我的数据库中有这样的用户,它应该继续提示用户输入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;
}
我的机器人应该可以帮助删除约会。
- 将提示输入用户的 nric(在 RetrieveAppt.cs 中)
- 随后,如果我的数据库中有这样的用户,它应该继续提示用户输入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;
}