使用Oscova的数据库的聊天机器人

恬静的小魔龙 2020-12-11 10:42:42 7845

导言

使用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.ClickInputBox.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命名空间。

现在,如果它什么也不做,它的指令又有什么用呢?这个指令必须为我们做两件事。

  • 从数据库选择ageRick
  • 显示一条说明说明选择了这名用户

为此,我们将在方法主体中添加以下内容:

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表里面,其中员工的名称等于指定的namevalue值。

最后,通过调用Result.SendResponse方法传递文本响应。此响应,如MainWindow构造函数更改ValueResponseLabel.

总体指令代码应该如下所示:

[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数据库。

检验指令

我们将重复对前一个对话框所做的操作,并添加DatabaseUpdateByNameDialogBot.Dialogs将下列代码放置在//Dialogs go hereMainWindow构造函数

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更改将被传递到数据库,否则操作将被取消。

好吧,这就是所有的内容。谢谢你的阅读!

有问题还是反馈?在评论中让我知道。

声明:本文内容由易百纳平台入驻作者撰写,文章观点仅代表作者本人,不代表易百纳立场。如有内容侵权或者其他问题,请联系本站进行删除。
红包 85 7 评论 打赏
评论
0个
内容存在敏感词
手气红包
    易百纳技术社区暂无数据
相关专栏
置顶时间设置
结束时间
删除原因
  • 广告/SPAM
  • 恶意灌水
  • 违规内容
  • 文不对题
  • 重复发帖
打赏作者
易百纳技术社区
恬静的小魔龙
您的支持将鼓励我继续创作!
打赏金额:
¥1易百纳技术社区
¥5易百纳技术社区
¥10易百纳技术社区
¥50易百纳技术社区
¥100易百纳技术社区
支付方式:
微信支付
支付宝支付
易百纳技术社区微信支付
易百纳技术社区
打赏成功!

感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~

举报反馈

举报类型

  • 内容涉黄/赌/毒
  • 内容侵权/抄袭
  • 政治相关
  • 涉嫌广告
  • 侮辱谩骂
  • 其他

详细说明

审核成功

发布时间设置
发布时间:
是否关联周任务-专栏模块

审核失败

失败原因
备注
拼手气红包 红包规则
祝福语
恭喜发财,大吉大利!
红包金额
红包最小金额不能低于5元
红包数量
红包数量范围10~50个
余额支付
当前余额:
可前往问答、专栏板块获取收益 去获取
取 消 确 定

小包子的红包

恭喜发财,大吉大利

已领取20/40,共1.6元 红包规则

    易百纳技术社区