用户选择属性对话框时请求属性值

时间:2017-12-12 17:01:44

标签: c# botframework

我有一个枚举,我正在向用户显示以供选择。用户选择任何选项后,应要求用户为所选选项提供值。一旦提供了价值,另一个确认应该是询问用户是否想要提供更多选择。如果是,那么应该像之前一样显示相同的枚举对话框/提示。如果否,则继续下一步操作。

我有enum对话框,用户也可以进行选择,但现在我不知道应该是什么方法来请求值,然后在哪里保存它然后提示确认继续再次显示枚举对话框。

    public enum OrderSearchOptions
{
    [Describe(Description = "Item Number")]
    [Prompt("Please provide {&}?")]
    ItemNumber,
    [Describe(Description = "Location")]
    Location,
    [Describe(Description = "Country")]
    Issuer,
    [Describe(Description = "Include Breakable")]
    IncludeBreakable,
    Status,
    [Describe(Description = "Packaging Requirement")]
}
[Serializable]
public class OrderAdvanceStepSearchQuery
{
    [Prompt("Please choose search attribute to start the search operation {||}")]
    public OrderSearchOptions? SearchOptions { get; set; }
}
public async Task StartAsync(IDialogContext context)
{
        await context.PostAsync($"Welcome to the Order helper!");
        var OrderFormDialog = FormDialog.FromForm(BuildOrderAdvanceStepSearchForm, FormOptions.PromptInStart);
        context.Call(OrderFormDialog, ResumeAfterOrdersFormDialog);
}   
private IForm<OrderAdvanceStepSearchQuery> BuildOrderAdvanceStepSearchForm()
{
    return new FormBuilder<OrderAdvanceStepSearchQuery>()
            .Build();
}
private async Task ResumeAfterRequiredDWPsFormDialog(IDialogContext context, IAwaitable<OrderAdvanceStepSearchQuery> result)
    {
        try
        {
            var searchQuery = await result;

            await context.PostAsync($"Ok. Searching for Orders...");

            var count = 100;

            if (count > 1)
            {
                await context.PostAsync($"I found total of 100 Orders");

                await context.PostAsync($"To get Order details, you will need to provide more info...");

            }
            else
            {
                await context.PostAsync($"I found the Order you were looking for...");
            }
        }
        catch (FormCanceledException ex)
        {
            string reply;

            if (ex.InnerException == null)
            {
                reply = "You have canceled the operation. Quitting from the Required DWP Search";
            }
            else
            {
                reply = $"Oops! Something went wrong :( Technical Details: {ex.InnerException.Message}";
            }

            await context.PostAsync(reply);
        }
        finally
        {
            context.Done<object>(null);
        }
    }

以下代码使用IDialog方法。

         public enum OrderSearchOptions
    {
        [Describe(Description = "Item Number")]
        [Prompt("Please provide {&}?")]
        ItemNumber,
        [Describe(Description = "Location")]
        Location,
        [Describe(Description = "Country")]
        Issuer,
        [Describe(Description = "Include Breakable")]
        IncludeBreakable,
        Status,
        [Describe(Description = "Packaging Requirement")]
    }   

    public enum Confirmation
    {
        Yes,
        No
    }

    public async Task StartAsync(IDialogContext context)
    {
        await context.PostAsync($"Welcome to the Order  helper!");

        PromptDialog.Choice(
           context: context,
           resume: ResumeAfterOrderSearchAttributeSelection,
           options: Enum.GetValues(typeof(OrderSearchOptions)).Cast<OrderSearchOptions>().ToArray(),
           prompt: "Please select any Order search attribute to start the search operation:",
           retry: "I didn't understand. Please try again.");
    }

    private async Task ResumeAfterOrderSearchAttributeSelection(IDialogContext context, IAwaitable<OrderSearchOptions> result)
    {
        var message = await result;

        await context.PostAsync("Please provide value for: " + message.ToString());

        context.Wait(OrderSearchAttributeValueReceived);
    }

    private async Task OrderSearchAttributeValueReceived(IDialogContext context, IAwaitable<object> result)
    {
        var message = await result;

        await context.PostAsync("Please provide value as: " + ((Activity)message).Text);

        PromptDialog.Choice(
           context: context,
           resume: ResumeAfterMoreOrderSearchAttributeConfirmation,
           options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
           prompt: "Do you want to search with more attributes:",
           retry: "I didn't understand. Please try again.");
    }

            private async Task ResumeAfterMoreOrderSearchAttributeConfirmation(IDialogContext context, IAwaitable<Confirmation> result)
    {
        var message = await result;

        if (message == Confirmation.Yes)
        {
            PromptDialog.Choice(
               context: context,
               resume: ResumeAfterOrderSearchAttributeSelection,
               options: Enum.GetValues(typeof(OrderSearchOptions)).Cast<OrderSearchOptions>().ToArray(),
               prompt: "Please select any Order  search attribute to start the search operation:",
               retry: "I didn't understand. Please try again.");
        }
        else
        {
            await context.PostAsync($"Ok. Searching for Order s...");

            var count = 100;

            if (count > 1)
            {
                await context.PostAsync($"I found total of 100 Order s");

                await context.PostAsync($"To get Order  details, you will need to provide more info...");

            }
            else
            {
                await context.PostAsync($"I found the Order  you were looking for...");

                await context.PostAsync($"Now I can provide you information related to your Order .");
            }
        }
    }

编辑:有关FormFlow中步骤方法的说明

我理解FormFlow中的Skip方法,但是要求用户按顺序提供信息,所以我想要一种机制,在imp字段之后,如果他/她想要提供更多信息,我们会接受用户确认。我对这种方法很好,我也理解它。但现在我试图看到实施不同方法的可能性。 步骤进行:

  1. Bot向用户发送选项(枚举/类)。
  2. 用户选择发送给Bot的选项。
  3. Bot接收该选项并要求用户 提供它的价值。
  4. 收到价值后(在哪里保存?)Bot询问用户是否愿意再次选择。
  5. 如果答案是&#34;是&#34;然后重复步骤1,如果答案是&#34;否&#34;然后移动 下一步。
  6. 将所有选项:值组合发送到DB 完成搜索操作。
  7. 我也试图通过FormFlow和IDialog选项来实现这一点。

    更好的IDialog方法但有2个问题。

    • 我无法获得Enum选项的正确名称,例如OrderNumber应显示为订单号。
    • 用户提供的任何选项都需要添加到类中,以便可以将其传递给DB / service。我对如何做到这一点没有任何想法。

    注意:我试图看看FormFlow和IDialog的可能性,用户决定他/她需要提供哪些属性信息。

        using System;
    using System.Threading.Tasks;
    using Microsoft.Bot.Builder.Dialogs;
    using Microsoft.Bot.Connector;
    using System.Collections.Generic;
    using System.Linq;
    using Microsoft.Bot.Builder.FormFlow;
    
    namespace Bot.Dialogs
    {
        [Serializable]
        public class OrderDialog : IDialog<object>
        {    
            public async Task StartAsync(IDialogContext context)
            {
                await context.PostAsync($"Welcome to the Order  helper!");
    
                await DisplayOrderSearchAttributeSelection(context);
            }
    
            private async Task DisplayOrderSearchAttributeSelection(IDialogContext context)
            {   
                PromptDialog.Choice(
                    context: context,
                    resume: ResumeAfterOrderSearchAttributeSelection,
                    options: Enum.GetValues(typeof(OrderSearchOptions)).Cast<OrderSearchOptions>().ToArray(),
                    prompt: "Please select any Order  search attribute to start the search operation:",
                    retry: "I didn't understand. Please try again.");
            }
    
            private async Task ResumeAfterOrderSearchAttributeSelection(IDialogContext context, IAwaitable<OrderSearchOptions> result)
            {
                var message = await result;
    
                if (message.ToString().ToLower() == "status")
                {
                    await DisplayOrderStatusCreatedAttributeSelection(context);
                }
                else
                {
                    await context.PostAsync("Please provide value for: " + message.ToString());
    
                    context.Wait(OrderSearchAttributeValueReceived);
                }
            }
    
            private async Task OrderSearchAttributeValueReceived(IDialogContext context, IAwaitable<object> result)
            {
                var message = await result as Activity;
    
                //set received value in the corresponding property.
    
                await MoreOrderSearchAttributeConfirmation(context);
            }
    
            private async Task MoreOrderSearchAttributeConfirmation(IDialogContext context)
            {
                PromptDialog.Choice(
                    context: context,
                    resume: ResumeAfterMoreOrderSearchAttributeConfirmation,
                    options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
                    prompt: "Do you want to search with more attributes:",
                    retry: "I didn't understand. Please try again.");
            }
    
            private async Task ResumeAfterMoreOrderSearchAttributeConfirmation(IDialogContext context, IAwaitable<Confirmation> result)
            {
                var message = await result;
    
                if (message == Confirmation.Yes)
                {
                    await DisplayOrderSearchAttributeSelection(context);
                }
                else
                {
                    //Take all the values provided by user and send to DB/Service for further processing.
    
                    await context.PostAsync($"Ok. Searching for Order s...");
    
                    var count = 100;
    
                    if (count > 1)
                    {
                        await context.PostAsync($"I found total of 100 Order s");
    
                        await context.PostAsync($"To get Order  details, you will need to provide more info...");
    
                        //some more logic needs to be written here
    
                    }
                    else
                    {
                        await context.PostAsync($"I found the Order  you were looking for...");
    
                        await context.PostAsync($"Now I can provide you information related to Consumer Package, Multi-Pack, Shelf Tray & Unit Load for this Order .");
                    }
                }
            }
    
            private async Task DisplayOrderStatusCreatedAttributeSelection(IDialogContext context)
            {
                PromptDialog.Choice(
                    context: context,
                    resume: ResumeAfterOrderStatusCreatedAttributeConfirmation,
                    options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
                    prompt: "Do you want to search for Status Created?",
                    retry: "I didn't understand. Please try again.");
            }
    
            private async Task ResumeAfterOrderStatusCreatedAttributeConfirmation(IDialogContext context, IAwaitable<Confirmation> result)
            {
                var message = await result;
    
                //set received value in the corresponding property.
    
                PromptDialog.Choice(
                    context: context,
                    resume: ResumeAfterOrderStatusPreliminaryAttributeConfirmation,
                    options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
                    prompt: "Do you want to search for Status Preliminary?",
                    retry: "I didn't understand. Please try again.");
            }
    
            private async Task ResumeAfterOrderStatusPreliminaryAttributeConfirmation(IDialogContext context, IAwaitable<Confirmation> result)
            {
                var message = await result;
    
                //set received value in the corresponding property.
    
                PromptDialog.Choice(
                    context: context,
                    resume: ResumeAfterOrderStatusCompletedAttributeConfirmation,
                    options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
                    prompt: "Do you want to search for Status Completed?",
                    retry: "I didn't understand. Please try again.");
            }
    
            private async Task ResumeAfterOrderStatusCompletedAttributeConfirmation(IDialogContext context, IAwaitable<Confirmation> result)
            {
                var message = await result;
    
                //set received value in the corresponding property.
    
                PromptDialog.Choice(
                    context: context,
                    resume: ResumeAfterOrderStatusSearchAttributesDone,
                    options: Enum.GetValues(typeof(Confirmation)).Cast<Confirmation>().ToArray(),
                    prompt: "Do you want to search for Status Expired?",
                    retry: "I didn't understand. Please try again.");
            }
    
            private async Task ResumeAfterOrderStatusSearchAttributesDone(IDialogContext context, IAwaitable<Confirmation> result)
            {
                var searchQuery = await result;
    
                await MoreOrderSearchAttributeConfirmation(context);
            }
    
        }
    }
    
    public enum OrderSearchOptions
    {
        [Describe(Description = "Order Number")]
        OrderNumber,
        [Describe(Description = "Location")]
        Location,
        Issuer,
        [Describe(Description = "Include Breakable")]
        IncludeBreakable,
        Status,
        [Describe(Description = "Packaging Requirement")]
        PackagingRequirement
    }
    
    public enum OrderStatus
    {
        [Describe(Description = "Status Created")]
        Created,
        Preliminary,
        Completed,
        Expired
    }
    

    修改:添加图片以供参考

    enter image description here enter image description here enter image description here enter image description here enter image description here

1 个答案:

答案 0 :(得分:1)

  

一旦提供了价值,其中&amp;如何保存它然后再次提示用户是否要继续或执行搜索。

要获得该值,您可以在ResumeAfterOrdersFormDialog中实现此功能,因为您可以像这样调用FormFlow:

context.Call(OrderFormDialog, ResumeAfterOrdersFormDialog);

您可以像这样编码:

private async Task ResumeAfterordersFormDialog(IDialogContext context, IAwaitable<object> result)
{
    var searchQuery = await result as OrderSearchQuery;
    var itemnumber = searchQuery.ItemNumber;
    var draft = searchQuery.Draft;

    context.Wait(ResumeAfterordersFormDialog);
}

要保存数据,您可以参考Manage state data。这取决于您希望保存数据的位置。就像在这里,我将数据保存在天蓝色存储表中:

private async Task ResumeAfterordersFormDialog(IDialogContext context, IAwaitable<object> result)
{
    var searchQuery = await result as OrderSearchQuery;
    var itemnumber = searchQuery.ItemNumber;
    var draft = searchQuery.Draft;

    // Save the information;
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
    CloudTableClient tableclient = storageAccount.CreateCloudTableClient();
    CloudTable table = tableclient.GetTableReference("SearchQuery");
    table.CreateIfNotExists();

    MyEntity entity = new MyEntity(itemnumber.ToString(), draft.ToString())
    {
        PartitionKey = itemnumber.ToString(),
        RowKey = DateTime.UtcNow.ToString("yyyyMMddhhmmss")
    };
    TableOperation insertOperation = TableOperation.Insert(entity);
    table.Execute(insertOperation);

    context.Wait(ResumeAfterordersFormDialog);
}

public class MyEntity : TableEntity
{
    public MyEntity(string item, string draft)
    {
        this.Item = item;
        this.Draft = draft;
    }

    public string Item { get; set; }
    public string Draft { get; set; }
}

顺便说一句,您需要在StorageConnectionString文件中添加从天蓝色存储中获取的Web.config

  

我看到有两个挑战我使用的枚举在显示选项时不使用描述或提示属性。就像枚举有ItemNumber但它应该显示为“项目编号”。哪个没有发生。

要显示枚举ItemNumber的“项目编号”,您可以使用以下代码:

public enum Item
{
    Item1,
    Item2,
    Item3,
    Item4,
}
public Item? ItemNumber { get; set; }

它会像这样呈现:

enter image description here

最后,如果您只想在确认后在FormFlow对话框中向用户显示更详细的信息,解决方案将与您的上一个案例相同:Skip displaying form fields based on user confirmation

顺便说一句,如果您使用普通IDialog而不是FormFlow对话框,则可以将获取和保存数据的代码移动到MessageReceivedAsync任务中。

如有任何疑问,请随时告诉我。

<强>更新

  

由于用户选择是动态的,我不知道如何将值设置为相应的类属性。

如果根据用户在第一步中的选择执行了不同的步骤,并且您希望使用FormFlow来实现该步骤,则可以尝试SetActive执行这些步骤。例如:

[Serializable]
public class OrderSearchQuery
{
    public enum SearchAttribute
    {
        OrderNumber,
        Location,
        Issuer,
        IncludeBreakable,
        Status,
        PackagingRequirement
    }

    public SearchAttribute? OrderSearchAttribute { get; set; }

    public string OrderNumber { get; set; }
    public string Location { get; set; }
    public string Issuer { get; set; }
    //And so on...

    private string SelectedItemName;

    private bool AvtivityConverter(string seletedItemname, string currentItemname)
    {
        return seletedItemname == currentItemname;
    }

    public IForm<OrderSearchQuery> BuildOrderAdvanceStepSearchForm()
    {
        return new FormBuilder<OrderSearchQuery>()
            .Field(new FieldReflector<OrderSearchQuery>(nameof(OrderSearchAttribute))
                .SetNext((value, state) =>
                {
                    var selection = (SearchAttribute)value;
                    SelectedItemName = selection.ToString();
                    return new NextStep();
                }))
             .Field(new FieldReflector<OrderSearchQuery>(nameof(OrderNumber))
                 .SetActive(state => AvtivityConverter(SelectedItemName, "OrderNumber")))
             .Field(new FieldReflector<OrderSearchQuery>(nameof(Location))
                 .SetActive(state => AvtivityConverter(SelectedItemName, "Location")))
             .Field(new FieldReflector<OrderSearchQuery>(nameof(Issuer))
                 .SetActive(state => AvtivityConverter(SelectedItemName, "Issuer")))
             //and so on...
             .Build();
    }
}