使用Oscova的数据库的聊天机器人
导言
使用Oscova(一个bot开发框架)创建一个SQL数据库的自然语言界面。
Oscova和Siml的区别在于,与Siml不同的是,Oscova不依赖于严格的模式。相反,它依赖于用户输入和保存的表达式之间的语义和语法。Oscova使开发人员能够创建利用对话、指令、文本和名词识别等概念和功能的机器人,以及不需要连接到任何在线API的额外优势。
话虽如此,如果您仍然倾向于编写严格的用户输入模式,那么Siml可能是你更好的选择
在本文中,我们将利用Oscova创建一个我们与机器人对话的employees数据库。
前提条件
- C#编程知识
- 使用SQL进行数据库管理。
- WPF应用程序和XAML的基本思想。
架构
Oscova的核心部分主要包括以下几个方面:
- 对话框:用以发送指令、对话、文本的输入框
- 意图:机器人在以下情况下执行/调用的操作:发送指令。
- 表达式:类似于用户输入的模板模式或示例。
实现步骤
- 首先,我们将使用所有必需的GUI元素创建一个WPF应用程序项目。
- 将基本库导入到我们的项目中。
- 创建一个用于与数据库交互的数据库实用类。
- 将GUI与机器人事件连接。
- 为几个数据库列创建实体识别器。
- 创建对话框和意图
- 测试我们的机器人。
如果发现任何概念难以掌握,只需打开项目,并参考代码。一旦浏览了项目的代码库,事情就会变得容易得多。
搭建WPF界面
让我们首先创建一个WPF项目,它将拥有我们与聊天机器人交互所需的所有GUI内容。
在整篇文章中,我将假设您正在使用VisualStudio。如果您是在Linux环境中,那么使用GTK的Mono应该就足够了,但我将在本文中继续使用VS。
- 启动VisualStudio
- 点击File,选择New然后选择Project..
- 在下面Templates,选择Windows然后选择WPF应用
- 命名项目NLI-Database-Oscova(如果您计划复制下面的代码,则非常重要)。NLI是缩写词自然语言接口.
WPF应用程序需要几个组件。
- 文本框:用于用户输入。
- 按钮:发送用户输入。
- 选项卡控件:第一个选项卡用于查看DataGrid(用于数据库),第二个选项卡用于查看JSON结果。
在解决方案资源管理器中双击MainWindow.xaml
并将XAML替换为下面的代码。
<c1></c1><Window x:Class="NLI_Database_Oscova.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Natural Language Interface using Oscova" Height="450" Width="560" WindowStartupLocation="CenterScreen">
<Grid>
<TabControl>
<TabItem Header="Interaction">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="35"/>
<RowDefinition Height="50*"/>
<RowDefinition Height="232*"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="414*"/>
<ColumnDefinition Width="100"/>
</Grid.ColumnDefinitions>
<TextBox Name="InputBox" TextAlignment="Center" CharacterCasing="Upper" KeyDown="InputBox_OnKeyDown"/>
<Button Name="ExecuteButton" Content="Evaluate" Grid.Column="1" Click="EvaluateButton_OnClick"/>
</Grid>
<Label Grid.Row="1" Name="ResponseLabel" Content="No Response Yet" VerticalContentAlignment="Top" />
<TabControl Grid.Row="2">
<TabItem Header="Table">
<DataGrid Name="EmployeeGrid" FontSize="14" />
</TabItem>
<TabItem Header="JSON Result">
<TextBox Name="ResultBox" IsReadOnly="True" AcceptsReturn="True" VerticalScrollBarVisibility="Auto" />
</TabItem>
</TabControl>
</Grid>
</TabItem>
<TabItem Header="Examples">
<TextBox Name="ExamplesBox" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" IsReadOnly="True"/>
</TabItem>
</TabControl>
</Grid>
</Window>
上面的代码添加了一个TabControl,其中有一个用于用户输入的文本框和一个Evluate(评估)按钮。
WPF应用程序应该如下所示:
导入Oscova和SQLite
现在,需要将Oscova导入到WPF应用程序中,Oscova是Syn.Bot框架的一部分,我们需要使用NuGet来引用包。
- 点击Tools,选择NuGet Package Manager并选择Package Manager Console
- 选择
Install-Package Syn.Bot
- Bot框架将与一些外部依赖项一起引用。
引用SQLite(用于数据库)
- 选择
Install-Package System.Data.SQLite
现在您已经成功地引用了所需的库。我们会继续用一些C#代码来开发。
数据库实用类
为了让对话变得简单,我们首先创建一个数据库实用程序类。右键单击Solution Explorer并选择Class。给类命名DatabaseUtility
替换DatabaseUtility
用以下方法初始化。
using System.Data.SQLite;
using System.IO;
namespace NLI_Database_Oscova
{
public class DatabaseUtility
{
const string DataSource = "EmployeesTable.db";
public SQLiteCommand Command { get; set; }
public SQLiteConnection Connection { get; set; }
public MainWindow Window { get; }
public DatabaseUtility(MainWindow window)
{
Window = window;
}
public void Initialize()
{
if (File.Exists(DataSource)) File.Delete(DataSource);
Connection = new SQLiteConnection { ConnectionString = "Data Source=" + DataSource };
Connection.Open();
ExecuteCommand("CREATE TABLE IF NOT EXISTS EMPLOYEES (ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, Name VARCHAR(100) NOT NULL, Role VARCHAR(10), Age INTEGER NOT NULL, Salary INTEGER NOT NULL);");
ExecuteCommand("INSERT INTO EMPLOYEES VALUES(1, 'Lincoln', 'Manager', 43, 54000)");
ExecuteCommand("INSERT INTO EMPLOYEES VALUES(2, 'George', 'CEO', 46, 75000)");
ExecuteCommand("INSERT INTO EMPLOYEES VALUES(3, 'Rick', 'Admin', 32, 18000)");
ExecuteCommand("INSERT INTO EMPLOYEES VALUES(4, 'Jorge', 'Engineer', 28, 35000)");
ExecuteCommand("INSERT INTO EMPLOYEES VALUES(5, 'Ivan', 'Tech', 23, 34000)");
ExecuteCommand("INSERT INTO EMPLOYEES VALUES(6, 'Mark', 'Tech', 25, 34000)");
ExecuteCommand("INSERT INTO EMPLOYEES VALUES(7, 'Vincent', 'Support', 21, 20000)");
ExecuteCommand("INSERT INTO EMPLOYEES VALUES(8, 'Wang', 'Support', 20, 20000)");
ExecuteCommand("INSERT INTO EMPLOYEES VALUES(9, 'Ahmed', 'Tech', 24, 34000)");
ExecuteCommand("INSERT INTO EMPLOYEES VALUES(10, 'Krishna', 'Admin', 25, 18000)");
}
private void ExecuteCommand(string commandText)
{
Command = new SQLiteCommand(Connection) { CommandText = commandText };
Command.ExecuteNonQuery();
}
}
}
等等,还有更多要补充的。内部DatabaseUtility
类添加以下方法。
public void Evaluate(string commandText)
{
Window.UpdateDataGrid(commandText);
}
public void UpdatePropertyByName(string employeeName, string propertyName, string propertyValue)
{
var updateString = $"UPDATE EMPLOYEES SET {propertyName}={propertyValue} WHERE UPPER(Name) LIKE UPPER('%{employeeName}%');";
var selectString = $"SELECT * FROM EMPLOYEES WHERE UPPER(Name) LIKE UPPER('{employeeName}%');";
Evaluate(updateString);
Evaluate(selectString);
}
public void UpdatePropertyById(string employeeId, string propertyName, string propertyValue)
{
var updateString = $"UPDATE EMPLOYEES SET {propertyName}={propertyValue} WHERE ID={employeeId};";
var selectString = $"SELECT * FROM EMPLOYEES WHERE ID={employeeId};";
Evaluate(updateString);
Evaluate(selectString);
}
public void PropertyByName(string employeeName, string propertyName)
{
var selectString = $"SELECT {propertyName} FROM Employees WHERE UPPER(Name) LIKE UPPER('%{employeeName}%');";
Evaluate(selectString);
}
public void PropertyByRole(string employeeRole, string propertyName)
{
var selectString = $"SELECT DISTINCT {propertyName} FROM Employees WHERE UPPER(Role) LIKE UPPER('%{employeeRole}%')";
Evaluate(selectString);
}
public void EmployeeByName(string employeeName)
{
var selectString = $"SELECT * FROM Employees WHERE UPPER(Name) LIKE UPPER('%{employeeName}%');";
Evaluate(selectString);
}
public void EmployeeByRole(string employeeRole)
{
var selectString = $"SELECT * FROM Employees WHERE UPPER(Role) LIKE UPPER('%{employeeRole}%');";
Evaluate(selectString);
}
public void Close()
{
Command.Dispose();
Connection.Dispose();
}
这个类MainWindow
作为其结构参数之一。我们在以后的项目中会用到它。
这个Initialize
方法帮助我们为我们的employees数据库。然后有一些帮助函数,如UpdatePropertyByName
, UpdatePropertyById
诸若此类。当调用某些意图时,我们的聊天机器人将使用这些函数。
您现在不必担心代码。这一切在后面的文章中都是有意义的。
主窗口
在Solution Explorer右击MainWindow.cs
并选择View Code.
添加以下命名空间:
using System;
using System.Data;
using System.Data.SQLite;
using System.Windows;
using System.Windows.Input;
using Syn.Bot.Oscova;
using Syn.Bot.Oscova.Collection;
using Syn.Bot.Oscova.Entities;
将下列属性添加到MainwWindow
班级。
public OscovaBot Bot { get; }
public DatabaseUtility DatabaseUtility { get; }
在构造函数中添加以下内容。
public MainWindow()
{
InitializeComponent();
Bot = new OscovaBot();
Bot.MainUser.ResponseReceived += (sender, args) =>
{
ResponseLabel.Content = args.Response.Text;
};
DatabaseUtility = new DatabaseUtility(this);
DatabaseUtility.Initialize();
UpdateDataGrid("SELECT * From Employees");
Bot.MainUser.Context.SharedData.Add(DatabaseUtility);
Bot.CreateRecognizer("set", new[] { "change", "update", "set" });
Bot.CreateRecognizer("property", new[] { "id", "name", "job", "age", "salary" });
//Parsers go here
//Dialogs go here
//Finally Train the Bot.
Bot.Train();
}
首先,我们使用实例化OscovaBot对象,分配聊天机器人属性。接下来我们处理ResponseReceived
的事件MainUser
若要显示响应的文本,使用ResponseLabel
.
然后,我们创建一个新的实例DatabaseUtility
类并调用Initialize()
方法。可以忽略UpdateDataGrid()
目前的方法。
因为我们需要DatabaseUtility
在整个聊天过程中,我们接下来将它添加到MainUser's
里面
接下来,我们利用重载CreateRecognizer
方法创建特定单词的识别器,如change, update诸若此类。
最后,我们添加Train()
方法。
注:Oscova需要Train()
方法在添加、创建或修改组件后调用一次。
有了这个,我们继续修复UpdateDataGrid()
功能。将下列代码添加到MainWindow
类。
public void UpdateDataGrid(string sql)
{
var dataSet = new DataSet();
var dataAdapter = new SQLiteDataAdapter(sql, DatabaseUtility.Connection);
dataAdapter.Fill(dataSet);
if (dataSet.Tables.Count > 0)
{
EmployeeGrid.ItemsSource = dataSet.Tables[0].DefaultView;
}
}
上面的方法使我们能够在每次SQL调用之后更新DataGrid。你可能已经注意到了DatabaseUtility
利用这种方法。
创建自定义实体识别器
到目前一切尚好。是时候创建一些实体识别器了。实体识别器只是IEntityRecognizer
接口,该接口读取normalized用户输入并返回匹配项的集合。
例如,数字识别器可能会使用正则表达式,并将所有匹配的数字作为实体的集合返回。
长话短说,我们数据库中的每一项都将被视为一个单独的实体。这允许用户输入中包含实体,从而使我们能够创建更好的表达式。
用户姓名识别器
我们将不再从头创建实体识别器,而是创建一个利用CreateRecognizer
重载方法,其第二个参数接受返回实体集合的函数。即EntityCollection
.
将下列方法添加到MainWindow
类。
private void CreateNameParser()
{
Bot.CreateRecognizer("Name", request =>
{
var requestText = request.NormalizedText;
var entities = new EntityCollection();
DatabaseUtility.Command.CommandText = "SELECT * FROM EMPLOYEES";
var reader = DatabaseUtility.Command.ExecuteReader();
while (reader.Read())
{
var name = reader["Name"].ToString();
var wordIndex = requestText.IndexOf(name, StringComparison.OrdinalIgnoreCase);
if (wordIndex != -1)
{
var entity = new Entity("Name")
{
Value = name,
Index = wordIndex
};
entities.Add(entity);
}
}
reader.Close();
return entities;
});
}
上面的代码创建了一个新的实体识别器。这个Name
是我们希望在数据库中引用用户姓名的实体类型名称。
这个requestText
保存用户请求文本的规范化值。规范化的值通常是相同的用户输入字符串,但是应用了过滤器。对此进行更多的讨论超出了本文的范围。
使用DatabaseUtility
类中的所有项进行迭代。name
我们数据库的列。然后,我们使用IndexOf
方法,以查看用户输入中是否存在该值。如果返回的索引值不是-1,则会找到该单词。当名字创建一个新的类型实体name
,并设置value的值为Index
。
注:在Oscova中,所有实体都必须指定匹配值第一次出现的起始索引。
用户等级识别器
同样,我们还将为每个员工的等级创建一个实体识别器。但是,与上面的实体识别器不同,我们只创建一个空识别器,并使用Role
列。
private void CreateRoleParser()
{
var parser = Bot.CreateRecognizer("Role");
DatabaseUtility.Command.CommandText = "SELECT * FROM EMPLOYEES";
var reader = DatabaseUtility.Command.ExecuteReader();
while (reader.Read())
{
var roleTitle = reader["Role"].ToString();
if (parser.Entries.Contains(roleTitle)) continue;
parser.Entries.Add(roleTitle);
}
reader.Close();
}
现在,让我们从MainWindow
构造函数将以下代码放置在//Parsers go here
在构造器中的注释。
//Parsers go here
CreateNameParser();
CreateRoleParser();
如果到目前为止,我希望您已经对命名实体识别器有了基本的了解。
处理按钮和文本框事件
传递用户输入InputBox
发送到Oscova进行Evaluate(评估),我们需要处理EvaluateButton.Click
和InputBox.KeyDown
事件。将下列代码添加到MainWindow
类。
private void EvaluateButton_OnClick(object sender, RoutedEventArgs e)
{
var message = string.IsNullOrEmpty(InputBox.Text) ? "clear" : InputBox.Text;
var result = Bot.Evaluate(message);
ResultBox.Text = result.Serialize();
result.Invoke();
InputBox.Clear();
}
上面的代码检查InputBox.Text
价值是空的还是空的。如果为空,则Clear命令被发送进行评估。
在之后评价结果返回,我们设置ResultBox.Text
属性保存序列化的JSON值,该值表示评价结果然后再调用Invoke
方法执行方法。
private void InputBox_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
EvaluateButton_OnClick(null, null);
}
}
上面的代码只在InputBox
按动Enter键时调用EvaluateButton_OnClick
方法。
创建对话框
到目前为止,我们一直在为我们的数据库机器人创建框架。现在跳出来,让我们开始实现基本dialog对话类。
dialog对话只是一个相关的集合指令集中在一起。你问我什么是指令?
指令是个指令!好吧,听起来可能不对。让我解释一下。指令是将用户输入映射到可执行操作。简单地说,在Oscova中,指令是一种在用户输入与表达式匹配时调用的方法。
Expression表达式另一方面,是一个模式或示例,取决于您如何编写它,类似于用户输入。
Oscova的评价系统将用户输入与所有输入进行比较执行。然后将表达式及其指令作为评价结果返回.
好了,代码老手,现在我们将创建我们的第一个对话框。
- 右键单击Solution Explorer,选择Add并选择New Folder
- 将文件夹命名为
Dialogs
- 右击文件夹,选择Add并选择Class..
- 给类命名
DatabaseEnquiryDialog
并选择Add.
为了转换DatabaseEnquiryDialog
对于一个Oscova对话框,您所要做的就是从Oscova的类中派生出Dialog
类,如下所示。
class DatabaseEnquiryDialog: Dialog
{
}
创造指令
我们的第一个指令是非常简单的。我们想要的是当用户说What is the age of Rick? (Rick(作为一个用户),我们希望聊天机器人返回相应的Age
列。
我们会制定我们的指令PropertyEnquiry
。为此,我们首先添加一个void
方法的DatabaseEnquiryDialog
类,如下所示。
public void PropertyEnquiry(Context context, Result result)
{
}
以上代码的一些信息。
- 指令方法必须始终具有
public
访问权限。 - 指令可以采用0,1(上下文或结果)或2(上下文和结果)参数。
- 指令必须至少有一个表达式。接下来介绍。
向指令中添加表达式
属性将表达式添加到指令的Expression
属性。有两种主要的表达式类型:
- 示例表达式--我们只编写一个输入示例,并使用大括号对实体进行可选注释。
- 模板表达式--我们编写用户输入,而不是注释,而是正确地指定实体类型。
示例表达式What is the age of Rick?看起来像:
[Expression("What is the {age} of {Rick}?")]
[Entity("property")]
[Entity("name")]
第一Entity
属性现在链接带注释的单词。age到实体类型property
还有这个词Rick链接到实体类型name
.
注:实体属性的顺序很重要,因为带注释的单词或短语的序列与Entity
属性。
模板表达式What is the age of Rick?看起来像:
[Expression("What is the @property of @name?")]
跟表达式不一样,模板表达式更容易编写,因为通过直接指定实体类型名称(以@为前缀)来避免注释。
为了本文,我们将添加一个example在我们的PropertyEnquiry
方法中。
[Expression("What is the {age} of {Rick}?")]
[Entity("property")]
[Entity("name")]
public void PropertyEnquiry(Context context, Result result)
{
}
注:所有Oscova属性都可以在Syn.Bot.Oscova.Attributes
命名空间。
现在,如果它什么也不做,它的指令又有什么用呢?这个指令必须为我们做两件事。
- 从数据库选择
age
栏Rick。 - 显示一条说明说明选择了这名用户
为此,我们将在方法主体中添加以下内容:
public void PropertyEnquiry(Context context, Result result)
{
var name = result.Entities.OfType("name").Value;
var property = result.Entities.OfType("property").Value;
var utility = context.SharedData.OfType<DatabaseUtility>();
utility.PropertyByName(name, property);
result.SendResponse($"{property} of Employee \"{name}\".");
}
上面的代码首先获取property
实体的name
的匹配值。所有提取的实体都存储在Result.Entities
。结果还发现,集合中的实体通常按用户输入中发生的顺序排列。
接下来,我们获取共享DatabaseUtility
对象并调用PropertyByName
方法。此方法通过选择指定的Employees
表里面,其中员工的名称等于指定的name
value值。
最后,通过调用Result.SendResponse
方法传递文本响应。此响应,如MainWindow
构造函数更改Value的ResponseLabel
.
总体指令代码应该如下所示:
[Expression("What is the {age} of {Rick}?")]
[Entity("property")]
[Entity("name")]
public void PropertyEnquiry(Context context, Result result)
{
var name = result.Entities.OfType("name").Value;
var property = result.Entities.OfType("property").Value;
var utility = context.SharedData.OfType<DatabaseUtility>();
utility.PropertyByName(name, property);
result.SendResponse($"{property} of Employee \"{name}\".");
}
测试指令
在我们继续测试指令我们需要把对话框添加到Oscova。在MainWindow
构造函数就在注释下面。//Dialogs go here
添加以下内容。
Bot.Dialogs.Add(new DatabaseEnquiryDialog());
给你。现在按F5运行应用程序,并在输入框中输入:
- What is the age of Rick?(Rick几岁了)
- What is the salary of Ahmed?(Ahmed的薪水多少)
如果输出一样!那么你已经成功地创造了你的第一个指令。
注:中的值。表达,我们消除了为不同属性编写冗余表达式的需要。
在附带说明中,如果您单击JSON Result
选项卡,您应该会看到评价结果.
{
"query": "WHAT IS THE AGE OF RICK?",
"sessionId": "78a8765f-3705-442d-a4ca-fe4dbd4c2e04",
"intents": [
{
"intent": "DatabaseEnquiryDialog.PropertyEnquiry",
"score": 0.9985714285714286
},
{
"intent": "DatabaseEnquiryDialog.EmployeeName",
"score": 0.59047619047619047
}
],
"entities": [
{
"entity": "AGE",
"type": "property"
},
{
"entity": "Rick",
"type": "Name"
}
],
"contexts": []
}
注:在不同的机器人设置中,指令的分数可能会有所不同。
更多指令
在我们进入下一个创建上下文意图的阶段之前。我们将继续增加一些指令表达式。
输入模式的意图:What is the salary of the CEO?(CEO薪水多少)
[Expression("What is the @property of @role")]
[Expression("@property of @role")]
public void PropertyByRole(Context context, Result result)
{
var role = result.Entities.OfType("role").Value;
var property = result.Entities.OfType("property").Value;
var utility = context.SharedData.OfType<DatabaseUtility>();
utility.PropertyByRole(role, property);
result.SendResponse($"{property} of \"{role}\".");
}
输入指令模式:Who is Wang?
[Expression("Find employee with the name @name")]
[Expression("Who is @name?")]
public void EmployeeName(Context context, Result result)
{
var name = result.Entities.OfType("name").Value;
var utility = context.SharedData.OfType<DatabaseUtility>();
utility.EmployeeByName(name);
result.SendResponse($"Employee(s) with the name {name}.");
}
输入指令模式:Who is the admin?
[Expression("Find employee whose role is @role")]
[Expression("Who is the @role?")]
public void EmployeeRole(Context context, Result result)
{
var role = result.Entities.OfType("role").Value;
var utility = context.SharedData.OfType<DatabaseUtility>();
utility.EmployeeByRole(role);
result.SendResponse($"Employee(s) with job role \"{role}\".");
}
语境指令
在本节中,我们将学习两个新概念。
- 语境指令
- 使用预先构建的系统实体。
尽管Oscova附带了数十个预先构建的系统实体,但我们将只使用其中的两个。
- 右键单击
Dialogs
文件夹,选择Add并选择Class.. - 给类命名
DatabaseUpdateByNameDialog
并选择Add.
就像前面的对话框一样,派生出DatabaseUpdateByNameDialog
从基地Dialog
类,如下所示。
class DatabaseUpdateByNameDialog : Dialog
{
}
此对话框中的意图结构如下:
- 允许用户更改员工财产价值(年龄、薪资)的意图
- 确定指令:如果用户说Yes提交对数据库的更改。
- 取消指令:如果用户说No提交操作被取消。
什么是语境?
上下文是表示用户输入的当前上下文的字符串值。在Oscova,上下文有名字和寿命。生命周期通常是上下文将持续的请求数。
为了更好的处理常量字符串值。我们将首先创建一个静态类来保存一个常量字符串值,我们将使用该值作为上下文项的名称。
向项目中添加一个新类并命名它DatabaseContext
.
static class DatabaseContext
{
public const string ConfirmUpdateByName = "ConfirmUpdateByName";
}
现在,允许用户对数据库进行更改的第一个指令。双击DatabaseUpdateByNameDialog.cs
并添加以下代码。
[Expression("@set @property of @name to @sys.number")]
public void ChangePropertyOfName(Context context, Result result)
{
context.Add(DatabaseContext.ConfirmUpdateByName);
var property = result.Entities.OfType("property");
var name = result.Entities.OfType("name");
var number = result.Entities.OfType<NumberEntity>();
context.SharedEntities.Add(property);
context.SharedEntities.Add(name);
context.SharedEntities.Add(number);
var propertyString = property.Value.ToLower();
if (propertyString != "age" && propertyString != "salary")
{
result.SendResponse($"{property} of {name} is readonly.");
return;
}
result.SendResponse($"Are you sure that you want to change {property} of {name} to {number}?");
}
这个Expression
在上面的代码中,启用以下命令:
- Change the age of Mark to 43(改变Mark的年龄为43)
- Set the salary of Krishna to 17000(设置Krishna的工资为17000)
在模板表达式中,值@sys.number
与数字匹配的系统实体类型。有大量的系统实体可供开发人员使用。您可以阅读Oscova的API文档以获得更多细节。
注意context.Add(DatabaseContext.ConfirmUpdateByName);
,通过将字符串值添加到上下文中,Bot在其下一个请求中将首先搜索指定上下文中的指令。
因为在下一次指令调用时,结果如果被清除,我们将使用Context.SharedEntities
属性将当前实体传递给下一个指令。
最后,当用户发送上述命令时,我们向用户发送一个请求确认的响应。
我们处理问题的方式来自用户的后续响应No。为此,我们将创建一个指令,如下所示。
[Expression("{no}")]
[Entity(Sys.Negative)]
[Context(DatabaseContext.ConfirmUpdateByName)]
public void ChangePropertyByNameDeclined(Context context, Result result)
{
context.SharedEntities.Clear();
result.SendResponse("Operating canceled.");
}
在上面的意图中有两件事需要注意。
第一[Entity(Sys.Negative)]
..Entity
属性指定类型的系统实体。@sys.negative
哪个匹配no 诸若此类。类的常量字段可以找到所有实体类型。Sys
类。
第二,[Context(DatabaseContext.ConfirmUpdateByName)]
显式指定此意图仅在ConfirmUpdateByName
背景。
我们创造了以下指令,用来响应来自用户的后续响应yes。
[Expression("{Yes}")]
[Entity(Sys.Positive)]
[Context(DatabaseContext.ConfirmUpdateByName)]
public void ChangePropertyConfirmed(Context context, Result result)
{
var property = context.SharedEntities.OfType("property");
var name = context.SharedEntities.OfType("name").Value;
var number = context.SharedEntities.OfType<NumberEntity>().Value;
var utility = context.SharedData.OfType<DatabaseUtility>();
utility.UpdatePropertyByName(name, property.Value, number.ToString(CultureInfo.InvariantCulture));
result.SendResponse($"{property} of {name} changed to {number}");
context.SharedEntities.Clear();
}
上述意图的表达式注释yes作为@sys.positive
实体类型来捕获积极的后续响应。就像ChangePropertyByNameDeclined
我们指定上述指令也必须在ConfirmUpdateByName
下面。
在方法体中,我们首先检索之前添加到SharedEntities
集合并将值传递给DatabaseUtility.UpdatePropertyByName
函数。
提交完成后,我们发送一个文本响应,说明已进行所请求的更改,并清除SharedEntities
收藏。
如果用户说yes或者类似的更改将被提交到Employees
数据库。
检验指令
我们将重复对前一个对话框所做的操作,并添加DatabaseUpdateByNameDialog
到Bot.Dialogs
将下列代码放置在//Dialogs go here
在MainWindow
构造函数
Bot.Dialogs.Add(new DatabaseUpdateByNameDialog());
按下F5发送指令Change the salary of Vincent to 24000(将Vincent的薪金改为24000).
如果一切顺利,应向你提出以下问题
Are you sure you want to change the salary of Vincent to 24000?(你确定你想把Vincent的薪水改为24000吗?)
如果你说yes更改将被传递到数据库,否则操作将被取消。
好吧,这就是所有的内容。谢谢你的阅读!
有问题还是反馈?在评论中让我知道。
- 分享
- 举报
-
浏览量:8835次2020-12-11 16:05:44
-
浏览量:9438次2020-12-10 18:35:14
-
浏览量:1236次2023-03-20 15:51:34
-
浏览量:988次2023-01-12 17:08:25
-
浏览量:9208次2020-12-12 14:56:13
-
浏览量:1393次2023-05-24 11:27:22
-
浏览量:8545次2017-12-01 16:55:09
-
浏览量:791次2023-03-14 10:09:41
-
浏览量:3750次2020-08-30 10:56:46
-
浏览量:1825次2022-11-18 14:29:30
-
浏览量:1407次2020-03-23 13:40:43
-
浏览量:1248次2022-12-09 10:51:45
-
浏览量:2421次2020-07-30 18:41:41
-
浏览量:1591次2022-11-04 14:02:58
-
浏览量:1268次2023-02-16 19:14:05
-
浏览量:3955次2021-04-05 12:09:13
-
浏览量:1324次2023-03-23 10:43:10
-
浏览量:1724次2022-09-30 15:13:05
-
浏览量:1805次2023-03-24 11:07:37
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
恬静的小魔龙
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明