.NET v3 ボットを .NET Core v4 ボットに移行するMigrate a .NET v3 bot to a .NET Core v4 bot

適用対象: SDK v4APPLIES TO: SDK v4

この記事では、新しい .NET Core プロジェクトでv3 ContosoHelpdeskChatBot を v4 ボットに変換します。In this article we'll convert the v3 ContosoHelpdeskChatBot into a v4 bot in a new .NET Core project. この変換は次の手順に分けられます。This conversion is broken down into these steps:

  1. テンプレートを使用して新しいプロジェクトを作成します。Create the new project using a template.
  2. 必要に応じて追加の NuGet パッケージをインストールします。Install additional NuGet packages as necessary.
  3. 自分のボットをカスタマイズし、Startup.cs ファイルを更新して、コントローラー クラスを更新します。Personalize your bot, update your Startup.cs file, and update your controller class.
  4. ボット クラスを更新します。Update your bot class.
  5. ご利用のダイアログとモデルをコピーして更新します。Copy over and update your dialogs and models.
  6. 最後の移行手順。Final porting step.

この変換の結果が、.NET Core v4 ContosoHelpdeskChatBot です。The result of this conversion is the .NET Core v4 ContosoHelpdeskChatBot. プロジェクトの種類を変換することなく、.NET Framework v4 ボットに移行するには、「.NET v3 ボットを Framework v4 ボットに移行する」を参照してください。To migrate to a .NET Framework v4 bot without converting the project type, see Migrate a .NET v3 bot to a .NET Framework v4 bot.

Bot Framework SDK v4 は、SDK v3 と同じ基になる REST API に基づいています。Bot Framework SDK v4 is based on the same underlying REST API as SDK v3. ただし、SDK v4 は、開発者がより柔軟にボットを制御できるように、以前のバージョンの SDK をリファクタリングしたものです。However, SDK v4 is a refactoring of the previous version of the SDK to allow developers more flexibility and control over their bots. この SDK の主な変更点は次のとおりです。Major changes in the SDK include:

  • 状態は、状態管理オブジェクトとプロパティ アクセサーを使用して管理されます。State is managed via state management objects and property accessors.
  • ターン ハンドラーの設定とターン ハンドラーへのアクティビティの受け渡しが変更されました。Setting up turn handler and passing activities to it has changed.
  • scorable は存在しなくなりました。Scorables no longer exist. ダイアログに制御を渡す前に、ターン ハンドラー内で "グローバル" コマンドの有無を確認できます。You can check for "global" commands in the turn handler, before passing control to your dialogs.
  • 以前のバージョンのものとは大きく異なる新しい Dialogs ライブラリ。A new Dialogs library that is very different from the one in the previous version. コンポーネント ダイアログ、ウォーターフォール ダイアログ、v4 用の FormFlow ダイアログのコミュニティ実装を使用して、古いダイアログを新しいダイアログ システムに変換する必要があります。You'll need to convert old dialogs to the new dialog system using component and waterfall dialogs and the community implementation of Formflow dialogs for v4.

具体的な変更点の詳細については、.NET SDK v3 と v4 の違いに関する記事をご覧ください。For more information about specific changes, see differences between the v3 and v4 .NET SDK.

テンプレートを使用して新しいプロジェクトを作成するCreate the new project using a template

注意

VSIXパッケージには、.net core 2.1 と .net core 3.1 の両方のバージョンの C# テンプレートが含まれています。The VSIX package includes both .NET Core 2.1 and .NET Core 3.1 versions of the C# templates. Visual Studio 2019 で新しいボットを作成するときは、.NET Core 3.1 テンプレートを使用する必要があります。When creating new bots in Visual Studio 2019, you should use the .NET Core 3.1 templates. 現在のボット サンプルでは .NET Core 3.1 テンプレートを使用しています。The current bot samples use .NET Core 3.1 templates. .NET Core 2.1 テンプレートを使用するサンプルは、BotBuilder-Samples リポジトリの 4.7-archive ブランチにあります。You can find the samples that use .NET Core 2.1 templates in the 4.7-archive branch of the BotBuilder-Samples repository. .NET Core 3.1 ボットを Azure にデプロイする方法の詳細については、「 bot を azure にデプロイする方法」を参照してください。For information about deploying .NET Core 3.1 bots to Azure, see how to deploy your bot to Azure.

  1. まだインストールしていない場合、C# 用の Bot Framework SDK v4 テンプレートをインストールします。If you haven't done so already, install the Bot Framework SDK v4 template for C#.
  2. Visual Studio を開いて、テンプレートから新しい Echo Bot プロジェクトを作成します。Open Visual Studio, and create a new Echo Bot project from the template. プロジェクトに ContosoHelpdeskChatBot という名前を付けます。Name your project ContosoHelpdeskChatBot.

追加の NuGet パッケージをインストールするInstall additional NuGet packages

このテンプレートによって、Microsoft.Bot.Builder パッケージや Microsoft.Bot.Connector パッケージなど、必要になるほとんどのパッケージがインストールされます。The template installs most of the packages you will need, including the Microsoft.Bot.Builder and Microsoft.Bot.Connector packages.

  1. Bot.Builder.Community.Dialogs.Formflow を追加します。Add Bot.Builder.Community.Dialogs.Formflow

    これは、v3 の FormFlow 定義ファイルから v4 のダイアログを構築するためのコミュニティ ライブラリです。This is a community library for building v4 dialogs from v3 Formflow definition files. その依存関係の 1 つとして、Microsoft.Bot.Builder.Dialogs があるので、これも自動的にインストールされます。It has Microsoft.Bot.Builder.Dialogs as one of its dependencies, so this is also installed for us.

  2. ログ記録をサポートするには、log4net を追加します。Add log4net to support logging.

ボットをカスタマイズするPersonalize your bot

  1. ボット ファイルの名前を Bots\EchoBot.cs から Bots\DialogBot.cs に変更して、EchoBot クラスの名前を DialogBot に変更します。Rename your bot file from Bots\EchoBot.cs to Bots\DialogBot.cs and rename the EchoBot class to DialogBot.
  2. コントローラーの名前を Controllers\BotController.cs から Controllers\MessagesController.cs に変更して、BotController クラスの名前を MessagesController に変更します。Rename your controller from Controllers\BotController.cs to Controllers\MessagesController.cs and rename the BotController class to MessagesController.

Startup.cs ファイルを更新するUpdate your Startup.cs file

状態とボットが受信アクティビティを受信する方法は変更されました。The way state and how the bot receives incoming activities has changed. v4 では状態管理インフラストラクチャの一部を自分で設定する必要があります。We have to set up parts of the state management infrastructure ourselves in v4. たとえば、v4 ではボット アダプターを使用して認証を処理し、アクティビティをボット コードに転送します。また、状態プロパティを事前に宣言しておきます。For instance, v4 uses a bot adapter to handle authentication and forward activities to your bot code, and we have declare our state properties up front.

DialogState の状態プロパティを作成します。これは、v4 のダイアログのサポートに必要になります。We'll create a state property for DialogState, which we now need for dialog support in v4. 依存関係挿入を使用して、必要な情報をコントローラーとボット コードに取得します。We'll use dependency injection to get necessary information to the controller and bot code.

Startup.cs で:In Startup.cs:

  1. using ステートメントを更新します。Update the using statements:

    using ContosoHelpdeskChatBot.Bots;
    using ContosoHelpdeskChatBot.Dialogs;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.Bot.Builder;
    using Microsoft.Bot.Builder.BotFramework;
    using Microsoft.Bot.Builder.Integration.AspNet.Core;
    using Microsoft.Bot.Connector.Authentication;
    using Microsoft.Extensions.DependencyInjection;
    
  2. このコンストラクターを削除します。Remove this constructor:

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }
    
  3. Configuration プロパティを削除します。Remove the Configuration property.

  4. 次のコードで ConfigureServices メソッドを更新します。Update the ConfigureServices method with this code:

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        
        // Create the credential provider to be used with the Bot Framework Adapter.
        services.AddSingleton<ICredentialProvider, ConfigurationCredentialProvider>();
    
        // Create the Bot Framework Adapter with error handling enabled. 
        services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();
    
        // Create the storage we'll be using for User and Conversation state. (Memory is great for testing purposes.) 
        services.AddSingleton<IStorage, MemoryStorage>();
        
        // Create the Conversation state. (Used by the Dialog system itself.)
        services.AddSingleton<ConversationState>();
    
        // Create the Root Dialog as a singleton
        services.AddSingleton<RootDialog>();
    
        // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
        services.AddTransient<IBot, DialogBot<RootDialog>>();
    }
    

この時点では、コンパイル時のエラーがあります。You are going to have compile time errors at this time. 次の手順でこれらを修正します。We'll fix them in the next steps.

MessagesController クラスMessagesController class

このクラスでは要求を処理します。This class handles a request. 依存関係の挿入では、ランタイム時のアダプターと IBot の実装が提供されます。Dependency Injection will provide the Adapter and IBot implementation at runtime. このテンプレート クラスは変更されません。This template class is unchanged.

これはボットが v4 でターンを開始する場所であり、v3 のメッセージ コントローラーとはまったく別のものです。This is where the bot starts a turn in v4, this is quite different form the v3 message controller. ボットのターン ハンドラー自体を除き、このほとんどは定型句と考えることができます。Except for the bot's turn handler itself, most of this can be thought of as boilerplate.

ボットのターン ハンドラーは、Bots\DialogBot.cs で定義されます。The bot turn handler will be defined in the Bots\DialogBot.cs.

CancelScorable クラスと GlobalMessageHandlersBotModule クラスを無視するIgnore the CancelScorable and GlobalMessageHandlersBotModule classes

スコアラブルは v4 には存在しないので、これらのクラスは無視するだけです。Since scorables don't exist in v4 we just ignore these classes. cancel メッセージに対処するために、ターン ハンドラーを更新します。We'll update the turn handler to react to a cancel message.

ボット クラスを更新するUpdate your bot class

v4 では、基本的に、ターン ハンドラーまたはメッセージ ループのロジックはボット ファイル内にあります。In v4, the turn handler or message loop logic is primarily in a bot file. ActivityHandler から派生させます。これは、一般的な種類のアクティビティのハンドラーを定義します。We're deriving from ActivityHandler, which defines handlers for common types of activities.

  1. Bots\DialogBots.cs ファイルを更新します。Update the Bots\DialogBots.cs file.

  2. using ステートメントを更新します。Update the using statements:

    using Microsoft.Bot.Builder;
    using Microsoft.Bot.Builder.Dialogs;
    using Microsoft.Bot.Schema;
    using System.Threading;
    using System.Threading.Tasks;
    
  3. ダイアログ用のジェネリック パラメーターを含めるように、DialogBot を更新します。Update DialogBot to include a generic parameter for the dialog.

    public class DialogBot<T> : ActivityHandler where T : Dialog
    
  4. これらのフィールドと、これらのフィールドを初期化するコンス トラクターを追加します。Add these fields and a constructor to initialize them. もう一度、ASP.NET で依存関係挿入を使用し、パラメーター値を取得します。Again, ASP.NET uses dependency injection to get the parameters values.

    protected readonly Dialog _dialog;
    protected readonly BotState _conversationState;
    
    public DialogBot(ConversationState conversationState, T dialog)
    {
        _conversationState = conversationState;
        _dialog = dialog;
    }
    
  5. メイン ダイアログを呼び出すように、OnMessageActivityAsync の実装を更新します。Update OnMessageActivityAsync implementation to invoke our main dialog. (この後すぐに Run 拡張メソッドを定義します)。(We'll define the Run extension method shortly.)

protected override async Task OnMessageActivityAsync(
    ITurnContext<IMessageActivity> turnContext,
    CancellationToken cancellationToken)
{
    // Run the Dialog with the new message Activity.
    await _dialog.Run(
        turnContext,
        _conversationState.CreateProperty<DialogState>("DialogState"),
        cancellationToken);
}
  1. OnTurnAsync を更新して、ターンの最後に会話状態を保存します。Update OnTurnAsync to save our conversation state at the end of the turn. v4 では、状態を永続化レイヤーに書き込むために、明示的にこれを行う必要があります。In v4, we have to do this explicitly to write state out to the persistence layer. ActivityHandler.OnTurnAsync メソッドでは、受信したアクティビティの種類に基づいて特定のアクティビティ ハンドラー メソッドが呼び出されます。そのため、基本メソッドの呼び出し後の状態を保存します。ActivityHandler.OnTurnAsync method calls specific activity handler methods, based on the type of activity received, so we save state after the call to the base method.

    public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken)
    {
        await base.OnTurnAsync(turnContext, cancellationToken);
    
        // Save any state changes that might have occured during the turn.
        await _conversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    }
    

Run 拡張メソッドを作成するCreate the Run extension method

拡張メソッドを作成します。この拡張メソッドで、ボットから最小限のコンポーネント ダイアログを実行するために必要なコードを統合します。We're creating an extension method to consolidate the code needed to run a bare component dialog from our bot.

DialogExtensions.cs ファイルを作成し、Run 拡張メソッドを実装します。Create a DialogExtensions.cs file and implement a Run extension method.

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Dialogs;

namespace ContosoHelpdeskChatBot
{
    public static class DialogExtensions
    {
        public static async Task Run(this Dialog dialog, ITurnContext turnContext, IStatePropertyAccessor<DialogState> accessor, CancellationToken cancellationToken = default(CancellationToken))
        {
            var dialogSet = new DialogSet(accessor);
            dialogSet.Add(dialog);

            var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken);
            var results = await dialogContext.ContinueDialogAsync(cancellationToken);
            if (results.Status == DialogTurnStatus.Empty)
            {
                await dialogContext.BeginDialogAsync(dialog.Id, null, cancellationToken);
            }
        }
    }
}

ダイアログをコピーして変換するCopy over and convert your dialogs

v4 SDK に移行するために、元の v3 ダイアログに多くの変更を加えます。We'll make many changes to the original v3 dialogs to migrate them to the v4 SDK. 現時点では、コンパイラ エラーは気にしないでください。Don't worry about the compiler errors for now. これらは変換が完了したら解決します。These will resolve once we've finished the conversion. 元のコードを必要以上に変更しないようにするために、移行の完了後もコンパイラの警告がいくつか残ります。In the interest of not modifying the original code more than necessary, there will remain some compiler warnings after we've finished the migration.

すべてのダイアログは、v3 の IDialog<object> インターフェイスを実装するのではなく、ComponentDialog から派生します。All of our dialogs will derive from ComponentDialog, instead of implementing the IDialog<object> interface of v3.

このボットには、変換する必要があるダイアログが 4 つあります。This bot has four dialogs that we need to convert:

ダイアログDialog DescriptionDescription
RootDialogRootDialog オプションを提示し、他のダイアログを開始します。Presents options and starts the other dialogs.
InstallAppDialogInstallAppDialog コンピューターにアプリをインストールする要求を処理します。Handles requests to install an app on a machine.
LocalAdminDialogLocalAdminDialog ローカル コンピューターの管理者権限の要求を処理します。Handles requests for local machine admin rights.
ResetPasswordDialogResetPasswordDialog パスワードをリセットする要求を処理します。Handles requests to reset your password.

scorables はもう存在しないので、CancelScorable クラスをコピーすることはありません。We won't copy over the CancelScorable class, as scorables no longer exist. ダイアログに制御を渡す前に、ターン ハンドラー内で "グローバル" コマンドの有無を確認できます。You can check for global commands in the turn handler, before passing control to your dialogs.

これらは入力を収集しますが、コンピューター上でこれらの操作を実行するわけではありません。These gather input, but do not perform any of these operations on your machine.

  1. 自分のプロジェクトで Dialogs フォルダーを作成します。Create a Dialogs folder in your project.
  2. v3 のプロジェクトのダイアログ ディレクトリからこれらのファイルを新しいダイアログ ディレクトリにコピーします。Copy these files from the v3 project's dialogs directory into your new dialogs directory.
    • InstallAppDialog.csInstallAppDialog.cs
    • LocalAdminDialog.csLocalAdminDialog.cs
    • ResetPasswordDialog.csResetPasswordDialog.cs
    • RootDialog.csRootDialog.cs

ソリューション全体のダイアログを変更するMake solution-wide dialog changes

  1. ソリューション全体で、出現するすべての IDialog<object>ComponentDialog に置き換えます。For the entire solution, Replace all occurrences of IDialog<object> with ComponentDialog.
  2. ソリューション全体で、出現するすべての IDialogContextDialogContext に置き換えます。For the entire solution, Replace all occurrences of IDialogContext with DialogContext.
  3. 各ダイアログ クラスで、[Serializable] 属性を削除します。For each dialog class, remove the [Serializable] attribute.

ダイアログ内の制御フローとメッセージングは同じ方法で処理されなくなったので、各ダイアログを変換するときにこれを変更する必要があります。Control flow and messaging within dialogs are no longer handled the same way, so we'll need to revise this as we convert each dialog.

操作Operation v3 コードv3 code v4 コードv4 code
ダイアログの開始を処理するHandle the start of your dialog IDialog.StartAsync を実装するImplement IDialog.StartAsync これをウォーターフォール ダイアログの最初のステップにするか、Dialog.BeginDialogAsync を実装するMake this the first step of a waterfall dialog, or implement Dialog.BeginDialogAsync
ダイアログの継続を処理するHandle continuation of your dialog IDialogContext.Wait を呼び出すCall IDialogContext.Wait ウォーターフォール ダイアログにステップを追加するか、Dialog.ContinueDialogAsync を実装するAdd additional steps to a waterfall dialog, or implement Dialog.ContinueDialogAsync
ユーザーにメッセージを送信するSend a message to the user IDialogContext.PostAsync を呼び出すCall IDialogContext.PostAsync ITurnContext.SendActivityAsync を呼び出すCall ITurnContext.SendActivityAsync
子ダイアログを開始するStart a child dialog IDialogContext.Call を呼び出すCall IDialogContext.Call DialogContext.BeginDialogAsync を呼び出すCall DialogContext.BeginDialogAsync
現在のダイアログが完了したことを通知するSignal that the current dialog has completed IDialogContext.Done を呼び出すCall IDialogContext.Done DialogContext.EndDialogAsync を呼び出すCall DialogContext.EndDialogAsync
ユーザーの入力を取得するGet the user's input IAwaitable<IMessageActivity> パラメーターを使用するUse an IAwaitable<IMessageActivity> parameter ウォーターフォール内からプロンプトを使用するか、ITurnContext.Activity を使用するUse a prompt from within a waterfall, or use ITurnContext.Activity

v4 コードに関する注意事項を次に示します。Notes about the v4 code:

  • ダイアログ コード内で、DialogContext.Context プロパティを使用して現在のターン コンテキストを取得します。Within dialog code, use the DialogContext.Context property to get the current turn context.
  • ウォーターフォール ステップには、DialogContext から派生した WaterfallStepContext パラメーターがあります。Waterfall steps have a WaterfallStepContext parameter, which derives from DialogContext.
  • 具象ダイアログ クラスおよびプロンプト クラスはすべて抽象 Dialog クラスから派生します。All concrete dialog and prompt classes derive from the abstract Dialog class.
  • コンポーネント ダイアログを作成するときに ID を割り当てます。You assign an ID when you create a component dialog. ダイアログ セットの各ダイアログには、そのセット内で一意の ID を割り当てる必要があります。Each dialog in a dialog set needs to be assigned an ID unique within that set.

ルート ダイアログを更新するUpdate the root dialog

このボットでは、ルート ダイアログがユーザーに一連のオプションからの選択を要求し、その選択に基づいて子ダイアログを開始します。In this bot, the root dialog prompts the user for a choice from a set of options, and then starts a child dialog based on that choice. 会話の有効期間中、これがループします。This then loops for the lifetime of the conversation.

  • メイン フローはウォーターフォール ダイアログとして設定できます。これは、v4 SDK の新しい概念です。We can set the main flow up as a waterfall dialog, which is a new concept in the v4 SDK. 固定された一連のステップが順番に実行されます。It will run through a fixed set of steps in order. 詳細については、「連続して行われる会話フローの実装」をご覧ください。For more information, see Implement sequential conversation flow.
  • プロンプトは、プロンプト クラスで処理されるようになりました。プロンプト クラスは、入力を要求し、最小限の処理と検証を実行して値を返す短い子ダイアログです。Prompting is now handled through prompt classes, which are short child dialogs that prompt for input, do some minimal processing and validation, and return a value. 詳細については、ダイアログ プロンプトを使用したユーザー入力の収集に関するページをご覧ください。For more information, see gather user input using a dialog prompt.

Dialogs/RootDialog.cs ファイルで、次の手順を実行します。In the Dialogs/RootDialog.cs file:

  1. using ステートメントを更新します。Update the using statements:

    using Microsoft.Bot.Builder;
    using Microsoft.Bot.Builder.Dialogs;
    using Microsoft.Bot.Builder.Dialogs.Choices;
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;
    
  2. HelpdeskOptions オプションを文字列のリストから選択肢のリストに変換する必要があります。We need to convert HelpdeskOptions options from a list of strings to a list of choices. これは選択プロンプトで使用されます。選択プロンプトは、(リスト内で) 選択された番号、選択された値、または選択肢のいずれかの同義語を有効な入力として受け入れます。This will be used with a choice prompt, which will accept the choice number (in the list), the choice value, or any of the choice's synonyms as valid input.

    private static List<Choice> HelpdeskOptions = new List<Choice>()
        {
            new Choice(InstallAppOption) { Synonyms = new List<string> { "install" } },
            new Choice(ResetPasswordOption) { Synonyms = new List<string> { "password" } },
            new Choice(LocalAdminOption)  { Synonyms = new List<string> { "admin" } }
        };
    
  3. コンストラクターを追加します。Add a constructor. このコードは、次の処理を実行します。This code does the following:

    • ダイアログの各インスタンスの作成時に ID を割り当てます。Each instance of a dialog is assigned an ID when it is created. ダイアログ ID は、ダイアログの追加先となるダイアログ セットに含まれます。The dialog ID is part of the dialog set to which the dialog is being added. ボットが MessageController クラス内でダイアログ オブジェクトを使用して初期化されたことを思い出してください。Recall that the bot was initialized with a dialog object in the MessageController class. ComponentDialog には、一連の独自のダイアログ ID が割り当てられた独自の内部ダイアログ セットがあります。Each ComponentDialog has its own internal dialog set, with its own set of dialog IDs.
    • 選択プロンプトなど、他のダイアログを子ダイアログとして追加します。It adds the other dialogs, including the choice prompt, as child dialogs. ここでは、各ダイアログ ID にクラス名を使用します。Here, we're just using the class name for each dialog ID.
    • 3 ステップのウォーターフォール ダイアログを定義します。It defines a three-step waterfall dialog. このダイアログはこの後すぐに実装します。We'll implement those in a moment.
      • ダイアログでは、まず、ユーザーに実行するタスクを選択するよう求めます。The dialog will first prompt the user to choose a task to perform.
      • 次に、その選択肢に関連付けられている子ダイアログを開始します。Then, start the child dialog associated with that choice.
      • 最後に、ダイアログ自体を再開します。And finally, restart itself.
    • ウォーターフォールの各ステップはデリゲートです。次にそれらを実装します。可能であれば、元のダイアログから既存のコードを取得します。Each step of the waterfall is a delegate, and we'll implement those next, taking existing code from the original dialog where we can.
    • コンポーネント ダイアログを開始すると、その initial dialog が開始されます。When you start a component dialog, it will start its initial dialog. 既定では、これはコンポーネント ダイアログに追加された最初の子ダイアログです。By default, this is the first child dialog added to a component dialog. InitialDialogId プロパティを明示的に設定します。つまり、メインのウォーターフォール ダイアログが、セットに追加する最初のダイアログである必要はありません。We're explicitly setting the InitialDialogId property, which means that the main waterfall dialog does not need to be the first one you add to the set. たとえば、最初にプロンプトを追加する方がよい場合は、実行時の問題を引き起こさずに、これを行うことが可能になります。For instance, if you prefer to add prompts first, this would allow you to do so without causing a run-time issue.
    public RootDialog()
        : base(nameof(RootDialog))
    {
        InitialDialogId = nameof(WaterfallDialog);
        AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
        {
            PromptForOptionsAsync,
            ShowChildDialogAsync,
            ResumeAfterAsync,
        }));
        AddDialog(new InstallAppDialog());
        AddDialog(new LocalAdminDialog());
        AddDialog(new ResetPasswordDialog());
        AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
    }
    
  4. StartAsync メソッドを削除できます。We can delete the StartAsync method. コンポーネント ダイアログが開始されると、"初期" ダイアログが自動的に開始されます。When a component dialog begins, it automatically starts its initial dialog. ここでは、コンストラクターで定義したウォーターフォール ダイアログがこれに該当します。In this case, that's the waterfall dialog we defined in the constructor. このダイアログも、その最初のステップで自動的に開始されます。That also automatically starts at its first step.

  5. MessageReceivedAsync メソッドと ShowOptions メソッドを削除し、それらをウォーターフォールの最初のステップに置き換えます。We will delete the MessageReceivedAsync and ShowOptions methods, and replace them with the first step of our waterfall. この 2 つのメソッドは、ユーザーにあいさつし、利用可能なオプションのいずれかを選択するよう求めていました。These two methods greeted the user and asked them to choose one of the available options.

    • ここでは、選択リストが表示されます。あいさつメッセージとエラー メッセージは、選択プロンプトの呼び出しでオプションとして提供されます。Here you can see the choice list and the greeting and error messages are provided as options in the call to our choice prompt.
    • 選択プロンプトが完了すると、ウォーターフォールは次のステップに進むため、ダイアログで次に呼び出すメソッドを指定する必要はありません。We don't have to specify the next method to call in the dialog, as the waterfall will continue to the next step when the choice prompt completes.
    • 選択プロンプトは、有効な入力を受け取るか、ダイアログ スタック全体がキャンセルされるまでループします。The choice prompt will loop until it receives valid input or the whole dialog stack is canceled.
    private async Task<DialogTurnResult> PromptForOptionsAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        // Prompt the user for a response using our choice prompt.
        return await stepContext.PromptAsync(
            nameof(ChoicePrompt),
            new PromptOptions()
            {
                Choices = HelpdeskOptions,
                Prompt = MessageFactory.Text(GreetMessage),
                RetryPrompt = MessageFactory.Text(ErrorMessage)
            },
            cancellationToken);
    }
    
  6. OnOptionSelected をウォーターフォールの 2 番目のステップに置き換えることができます。We can replace OnOptionSelected with the second step of our waterfall. 置換後も、ユーザーの入力に基づいて子ダイアログを開始します。We still start a child dialog based on the user's input.

    • 選択プロンプトは FoundChoice 値を返します。The choice prompt returns a FoundChoice value. これは、ステップ コンテキストの Result プロパティに示されます。This shows up in the step context's Result property. ダイアログ スタックでは、すべての戻り値をオブジェクトとして扱います。The dialog stack treats all return values as objects. 戻り値がダイアログのいずれかから返されている場合、そのオブジェクトがどの種類の値であるかがわかります。If the return value is from one of your dialogs, then you know what type of value the object is. 各プロンプトの種類で返される値の一覧については、プロンプトの種類に関するセクションをご覧ください。See prompt types for a list what each prompt type returns.
    • 選択プロンプトは例外をスローしないため、try-catch ブロックを削除できます。Since the choice prompt won't throw an exception, we can remove the try-catch block.
    • このメソッドが常に適切な値を返すように、フォールスルーを追加する必要があります。We need to add a fall through so that this method always returns an appropriate value. このコードへの到達は回避する必要がありますが、到達しても、ダイアログを "正常に失敗にする" ことができます。This code should never get hit, but if it does it will allow the dialog to "fail gracefully".
    private async Task<DialogTurnResult> ShowChildDialogAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        // string optionSelected = await userReply;
        var optionSelected = (stepContext.Result as FoundChoice).Value;
    
        switch (optionSelected)
        {
            case InstallAppOption:
                //context.Call(new InstallAppDialog(), this.ResumeAfterOptionDialog);
                //break;
                return await stepContext.BeginDialogAsync(
                    nameof(InstallAppDialog),
                    cancellationToken);
            case ResetPasswordOption:
                //context.Call(new ResetPasswordDialog(), this.ResumeAfterOptionDialog);
                //break;
                return await stepContext.BeginDialogAsync(
                    nameof(ResetPasswordDialog),
                    cancellationToken);
            case LocalAdminOption:
                //context.Call(new LocalAdminDialog(), this.ResumeAfterOptionDialog);
                //break;
                return await stepContext.BeginDialogAsync(
                    nameof(LocalAdminDialog),
                    cancellationToken);
        }
    
        // We shouldn't get here, but fail gracefully if we do.
        await stepContext.Context.SendActivityAsync(
            "I don't recognize that option.",
            cancellationToken: cancellationToken);
        // Continue through to the next step without starting a child dialog.
        return await stepContext.NextAsync(cancellationToken: cancellationToken);
    }
    
  7. 最後に、古い ResumeAfterOptionDialog メソッドをウォーターフォールの最後のステップに置き換えます。Finally, replace the old ResumeAfterOptionDialog method with the last step of our waterfall.

    • 元のダイアログで行っていたように、ダイアログを終了してチケット番号を返すのではなく、スタック上で元のインスタンスをそれ自体の新しいインスタンスに置き換えることでウォーターフォールを再開します。Instead of ending the dialog and returning the ticket number as we did in the original dialog, we're restarting the waterfall by replacing on the stack the original instance with a new instance of itself. これが可能なのは、元のアプリでは常に戻り値 (チケット番号) が無視され、ルート ダイアログが再開されていたためです。We can do this, since the original app always ignored the return value (the ticket number) and restarted the root dialog.
    private async Task<DialogTurnResult> ResumeAfterAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        try
        {
            //var message = await userReply;
            var message = stepContext.Context.Activity;
    
            var ticketNumber = new Random().Next(0, 20000);
            //await context.PostAsync($"Thank you for using the Helpdesk Bot. Your ticket number is {ticketNumber}.");
            await stepContext.Context.SendActivityAsync(
                $"Thank you for using the Helpdesk Bot. Your ticket number is {ticketNumber}.",
                cancellationToken: cancellationToken);
    
            //context.Done(ticketNumber);
        }
        catch (Exception ex)
        {
            // await context.PostAsync($"Failed with message: {ex.Message}");
            await stepContext.Context.SendActivityAsync(
                $"Failed with message: {ex.Message}",
                cancellationToken: cancellationToken);
    
            // In general resume from task after calling a child dialog is a good place to handle exceptions
            // try catch will capture exceptions from the bot framework awaitable object which is essentially "userReply"
            logger.Error(ex);
        }
    
        // Replace on the stack the current instance of the waterfall with a new instance,
        // and start from the top.
        return await stepContext.ReplaceDialogAsync(
            nameof(WaterfallDialog),
            cancellationToken: cancellationToken);
    }
    

アプリのインストール ダイアログを更新するUpdate the install app dialog

アプリのインストール ダイアログでは、いくつかの論理タスクを実行します。これを 4 ステップのウォーターフォール ダイアログとして設定します。The install app dialog performs a few logical tasks, which we'll set up as a 4-step waterfall dialog. 既存のコードをウォーターフォール ステップにどのように組み込むかは、ダイアログごとの論理的な作業です。How you factor existing code into waterfall steps is a logical exercise for each dialog. ステップごとに、そのコードが含まれていた元のメソッドに留意します。For each step, the original method the code came from is noted.

  1. ユーザーに検索文字列を要求します。Asks the user for a search string.
  2. 一致候補をデータベースに照会します。Queries a database for potential matches.
    • 一致候補が 1 つの場合は、これを選択して続行します。If there is one hit, select this and continue.
    • 一致候補が複数ある場合は、ユーザーにいずれかを選択するよう求めます。If there are multiple hits, it asks the user to choose one.
    • 一致候補がない場合、ダイアログは終了します。If there are no hits, the dialog exits.
  3. ユーザーにアプリをインストールするコンピューター名の入力を求めます。Asks the user for a machine to install the app on.
  4. 情報をデータベースに書き込み、確認メッセージを送信します。Writes the information to a database and sends a confirmation message.

Dialogs/InstallAppDialog.cs ファイルで、次の手順を実行します。In the Dialogs/InstallAppDialog.cs file:

  1. using ステートメントを更新します。Update the using statements:

    using ContosoHelpdeskChatBot.Models;
    using Microsoft.Bot.Builder;
    using Microsoft.Bot.Builder.Dialogs;
    using Microsoft.Bot.Builder.Dialogs.Choices;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    
  2. 収集した情報を追跡するために使用するキーの定数を定義します。Define a constant for the key we'll use to track collected information.

    // Set up keys for managing collected information.
    private const string InstallInfo = "installInfo";
    
  3. コンストラクターを追加し、コンポーネントのダイアログ セットを初期化します。Add a constructor and initialize the component's dialog set.

    public InstallAppDialog()
        : base(nameof(InstallAppDialog))
    {
        // Initialize our dialogs and prompts.
        InitialDialogId = nameof(WaterfallDialog);
        AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] {
            GetSearchTermAsync,
            ResolveAppNameAsync,
            GetMachineNameAsync,
            SubmitRequestAsync,
        }));
        AddDialog(new TextPrompt(nameof(TextPrompt)));
        AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
    }
    
  4. StartAsync をウォーターフォールの最初のステップに置き換えることができます。We can replace StartAsync with the first step of our waterfall.

    • 状態を自分で管理する必要があるので、ダイアログの状態でアプリのインストール オブジェクトを追跡します。We have to manage state ourselves, so we'll track the install app object in dialog state.
    • ユーザーに入力を求めるメッセージは、プロンプトの呼び出しでのオプションになります。The message asking the user for input becomes an option in the call to the prompt.
    private async Task<DialogTurnResult> GetSearchTermAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        // Create an object in dialog state in which to track our collected information.
        stepContext.Values[InstallInfo] = new InstallApp();
    
        // Ask for the search term.
        return await stepContext.PromptAsync(
            nameof(TextPrompt),
            new PromptOptions
            {
                Prompt = MessageFactory.Text("Ok let's get started. What is the name of the application? "),
            },
            cancellationToken);
    }
    
  5. appNameAsyncmultipleAppsAsync をウォーターフォールの 2 番目のステップに置き換えることができます。We can replace appNameAsync and multipleAppsAsync with the second step of our waterfall.

    • ユーザーの最後のメッセージを単に確認するのではなく、プロンプトの結果を取得します。We're getting the prompt result now, instead of just looking at the user's last message.
    • データベース クエリと if ステートメントは、appNameAsync の場合と同様に構成されています。The database query and if statements are organized the same as in appNameAsync. if ステートメントの各ブロック内のコードは、v4 ダイアログで動作するように更新されました。The code in each block of the if statement has been updated to work with v4 dialogs.
      • 一致候補が 1 つの場合は、ダイアログの状態を更新し、次のステップに進みます。If we have one hit, we'll update dialog state and continue with the next step.
      • 一致候補が複数ある場合は、選択プロンプトを使用して、オプションの一覧から選択するようユーザーに求めます。If we have multiple hits, we'll use our choice prompt to ask the user to choose from the list of options. つまり、multipleAppsAsync は削除できます。This means we can just delete multipleAppsAsync.
      • 一致候補がない場合は、このダイアログを終了し、ルート ダイアログに null を返します。If we have no hits, we'll end this dialog and return null to the root dialog.
    private async Task<DialogTurnResult> ResolveAppNameAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        // Get the result from the text prompt.
        var appname = stepContext.Result as string;
    
        // Query the database for matches.
        var names = await this.GetAppsAsync(appname);
    
        if (names.Count == 1)
        {
            // Get our tracking information from dialog state and add the app name.
            var install = stepContext.Values[InstallInfo] as InstallApp;
            install.AppName = names.First();
    
            return await stepContext.NextAsync();
        }
        else if (names.Count > 1)
        {
            // Ask the user to choose from the list of matches.
            return await stepContext.PromptAsync(
                nameof(ChoicePrompt),
                new PromptOptions
                {
                    Prompt = MessageFactory.Text("I found the following applications. Please choose one:"),
                    Choices = ChoiceFactory.ToChoices(names),
                },
                cancellationToken);
        }
        else
        {
            // If no matches, exit this dialog.
            await stepContext.Context.SendActivityAsync(
                $"Sorry, I did not find any application with the name '{appname}'.",
                cancellationToken: cancellationToken);
    
            return await stepContext.EndDialogAsync(null, cancellationToken);
        }
    }
    
  6. appNameAsync では、クエリの解決後にユーザーにコンピューター名の入力も求めていました。appNameAsync also asked the user for their machine name after it resolved the query. ウォーターフォールの次のステップに、ロジックのこの部分を取り込みます。We'll capture that portion of the logic in the next step of the waterfall.

    • 繰り返しますが、v4 では状態を自分で管理する必要があります。Again, in v4 we have to manage state ourselves. ここで唯一注意が必要なことは、前のステップで 2 つの異なるロジック分岐を通過してこのステップに到達できることです。The only tricky thing here is that we can get to this step through two different logic branches in the previous step.
    • 今回は別のオプションを指定して、以前と同じテキスト プロンプトを使ってユーザーにコンピューター名の入力を求めます。We'll ask the user for a machine name using the same text prompt as before, just supplying different options this time.
    private async Task<DialogTurnResult> GetMachineNameAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        // Get the tracking info. If we don't already have an app name,
        // Then we used the choice prompt to get it in the previous step.
        var install = stepContext.Values[InstallInfo] as InstallApp;
        if (install.AppName is null)
        {
            install.AppName = (stepContext.Result as FoundChoice).Value;
        }
    
        // We now need the machine name, so prompt for it.
        return await stepContext.PromptAsync(
            nameof(TextPrompt),
            new PromptOptions
            {
                Prompt = MessageFactory.Text(
                    $"Found {install.AppName}. What is the name of the machine to install application?"),
            },
            cancellationToken);
    }
    
  7. machineNameAsync のロジックは、ウォーターフォールの最後のステップにまとめられています。The logic from machineNameAsync is wrapped up in the final step of our waterfall.

    • テキスト プロンプトの結果からコンピューター名を取得し、ダイアログの状態を更新します。We retrieve the machine name from the text prompt result and update dialog state.
    • サポート コードは別のプロジェクトにあるため、データベースを更新する呼び出しを削除します。We are removing the call to update the database, as the supporting code is in a different project.
    • その後、成功メッセージをユーザーに送信し、ダイアログを終了します。Then we're sending the success message to the user and ending the dialog.
    private async Task<DialogTurnResult> SubmitRequestAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        var install = default(InstallApp);
        if (stepContext.Reason != DialogReason.CancelCalled)
        {
            // Get the tracking info and add the machine name.
            install = stepContext.Values[InstallInfo] as InstallApp;
            install.MachineName = stepContext.Context.Activity.Text;
    
            //TODO: Save to this information to the database.
        }
    
        await stepContext.Context.SendActivityAsync(
            $"Great, your request to install {install.AppName} on {install.MachineName} has been scheduled.",
            cancellationToken: cancellationToken);
    
        return await stepContext.EndDialogAsync(null, cancellationToken);
    }
    
  8. データベースの呼び出しをシミュレートするために、データベースではなく静的リストを照会するように getAppsAsync をモックアップします。To simulate the database call, we mock up getAppsAsync to query a static list, instead of the database.

    private async Task<List<string>> GetAppsAsync(string Name)
    {
        var names = new List<string>();
    
        // Simulate querying the database for applications that match.
        return (from app in AppMsis
                where app.ToLower().Contains(Name.ToLower())
                select app).ToList();
    }
    
    // Example list of app names in the database.
    private static readonly List<string> AppMsis = new List<string>
    {
        "µTorrent 3.5.0.44178",
        "7-Zip 17.1",
        "Ad-Aware 9.0",
        "Adobe AIR 2.5.1.17730",
        "Adobe Flash Player (IE) 28.0.0.105",
        "Adobe Flash Player (Non-IE) 27.0.0.130",
        "Adobe Reader 11.0.14",
        "Adobe Shockwave Player 12.3.1.201",
        "Advanced SystemCare Personal 11.0.3",
        "Auslogics Disk Defrag 3.6",
        "avast! 4 Home Edition 4.8.1351",
        "AVG Anti-Virus Free Edition 9.0.0.698",
        "Bonjour 3.1.0.1",
        "CCleaner 5.24.5839",
        "Chmod Calculator 20132.4",
        "CyberLink PowerDVD 17.0.2101.62",
        "DAEMON Tools Lite 4.46.1.328",
        "FileZilla Client 3.5",
        "Firefox 57.0",
        "Foxit Reader 4.1.1.805",
        "Google Chrome 66.143.49260",
        "Google Earth 7.3.0.3832",
        "Google Toolbar (IE) 7.5.8231.2252",
        "GSpot 2701.0",
        "Internet Explorer 903235.0",
        "iTunes 12.7.0.166",
        "Java Runtime Environment 6 Update 17",
        "K-Lite Codec Pack 12.1",
        "Malwarebytes Anti-Malware 2.2.1.1043",
        "Media Player Classic 6.4.9.0",
        "Microsoft Silverlight 5.1.50907",
        "Mozilla Thunderbird 57.0",
        "Nero Burning ROM 19.1.1005",
        "OpenOffice.org 3.1.1 Build 9420",
        "Opera 12.18.1873",
        "Paint.NET 4.0.19",
        "Picasa 3.9.141.259",
        "QuickTime 7.79.80.95",
        "RealPlayer SP 12.0.0.319",
        "Revo Uninstaller 1.95",
        "Skype 7.40.151",
        "Spybot - Search & Destroy 1.6.2.46",
        "SpywareBlaster 4.6",
        "TuneUp Utilities 2009 14.0.1000.353",
        "Unlocker 1.9.2",
        "VLC media player 1.1.6",
        "Winamp 5.56 Build 2512",
        "Windows Live Messenger 2009 16.4.3528.331",
        "WinPatrol 2010 31.0.2014",
        "WinRAR 5.0",
    };
    

ローカル管理者ダイアログを更新するUpdate the local admin dialog

v3 では、このダイアログでユーザーにあいさつし、FormFlow ダイアログを開始して、結果をデータベースに保存していました。In v3, this dialog greeted the user, started the Formflow dialog, and then saved the result off to a database. これは 2 ステップのウォーターフォールに簡単に変換できます。This translates easily into a two-step waterfall.

  1. using ステートメントを更新します。Update the using statements. このダイアログには、v3 の FormFlow ダイアログが含まれています。Note that this dialog includes a v3 Formflow dialog. v4 では、FormFlow コミュニティ ライブラリを使用できます。In v4 we can use the community Formflow library.

    using Bot.Builder.Community.Dialogs.FormFlow;
    using ContosoHelpdeskChatBot.Models;
    using Microsoft.Bot.Builder.Dialogs;
    using System.Threading;
    using System.Threading.Tasks;
    
  2. 結果はダイアログの状態で提供されるので、LocalAdmin のインスタンス プロパティを削除できます。We can remove the instance property for LocalAdmin, as the result will be available in dialog state.

  3. コンストラクターを追加し、コンポーネントのダイアログ セットを初期化します。Add a constructor and initialize the component's dialog set. FormFlow ダイアログも同じ方法で作成されます。The Formflow dialog is created in the same way. コンストラクター内でコンポーネントのダイアログ セットに追加するだけです。We're just adding it to the dialog set of our component in the constructor.

    public LocalAdminDialog() : base(nameof(LocalAdminDialog))
    {
        InitialDialogId = nameof(WaterfallDialog);
        AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
        {
            BeginFormflowAsync,
            SaveResultAsync,
        }));
        AddDialog(FormDialog.FromForm(BuildLocalAdminForm, FormOptions.PromptInStart));
    }
    
  4. StartAsync をウォーターフォールの最初のステップに置き換えることができます。We can replace StartAsync with the first step of our waterfall. コンストラクター内で FormFlow を既に作成しているので、他の 2 つのステートメントはこれに変換されます。We already created the Formflow in the constructor, and the other two statements translate to this. なお、FormBuilder では、生成されたダイアログの ID としてモデルの種類の名前が割り当てられますが、このモデルでは LocalAdminPrompt がそれにあたります。Note that FormBuilder assigns the model's type name as the ID of the generated dialog, which is LocalAdminPrompt for this model.

    private async Task<DialogTurnResult> BeginFormflowAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        await stepContext.Context.SendActivityAsync("Great I will help you request local machine admin.");
    
        // Begin the Formflow dialog.
        return await stepContext.BeginDialogAsync(
            nameof(LocalAdminPrompt),
            cancellationToken: cancellationToken);
    }
    
  5. ResumeAfterLocalAdminFormDialog をウォーターフォールの 2 番目のステップに置き換えることができます。We can replace ResumeAfterLocalAdminFormDialog with the second step of our waterfall. インスタンス プロパティからではなく、ステップ コンテキストから戻り値を取得する必要があります。We have to get the return value from the step context, instead of from an instance property.

    private async Task<DialogTurnResult> SaveResultAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        // Get the result from the Formflow dialog when it ends.
        if (stepContext.Reason != DialogReason.CancelCalled)
        {
            var admin = stepContext.Result as LocalAdminPrompt;
    
            //TODO: Save to this information to the database.
        }
    
        return await stepContext.EndDialogAsync(null, cancellationToken);
    }
    
  6. FormFlow でインスタンス プロパティを更新しない点を除き、BuildLocalAdminForm の変更はほとんどありません。BuildLocalAdminForm remains largely the same, except we don't have the Formflow update the instance property.

    // Nearly the same as before.
    private IForm<LocalAdminPrompt> BuildLocalAdminForm()
    {
        // Here's an example of how validation can be used with FormBuilder.
        return new FormBuilder<LocalAdminPrompt>()
            .Field(nameof(LocalAdminPrompt.MachineName),
            validate: async (state, value) =>
            {
                var result = new ValidateResult { IsValid = true, Value = value };
                //add validation here
    
                //this.admin.MachineName = (string)value;
                return result;
            })
            .Field(nameof(LocalAdminPrompt.AdminDuration),
            validate: async (state, value) =>
            {
                var result = new ValidateResult { IsValid = true, Value = value };
                //add validation here
    
                //this.admin.AdminDuration = Convert.ToInt32((long)value) as int?;
                return result;
            })
            .Build();
    }
    

パスワードのリセット ダイアログを更新するUpdate the reset password dialog

v3 では、このダイアログでユーザーにあいさつし、パス コードでユーザーを承認して、失敗にするかまたは FormFlow ダイアログを開始した後、パスワードをリセットしていました。In v3, this dialog greeted the user, authorized the user with a pass code, failed out or started the Formflow dialog, and then reset the password. これもウォーターフォールに適切に変換できます。This still translates well into a waterfall.

  1. using ステートメントを更新します。Update the using statements. このダイアログには、v3 の FormFlow ダイアログが含まれています。Note that this dialog includes a v3 Formflow dialog. v4 では、FormFlow コミュニティ ライブラリを使用できます。In v4 we can use the community Formflow library.

    using Bot.Builder.Community.Dialogs.FormFlow;
    using ContosoHelpdeskChatBot.Models;
    using Microsoft.Bot.Builder.Dialogs;
    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
  2. コンストラクターを追加し、コンポーネントのダイアログ セットを初期化します。Add a constructor and initialize the component's dialog set. FormFlow ダイアログも同じ方法で作成されます。The Formflow dialog is created in the same way. コンストラクター内でコンポーネントのダイアログ セットに追加するだけです。We're just adding it to the dialog set of our component in the constructor.

    public ResetPasswordDialog()
        : base(nameof(ResetPasswordDialog))
    {
        InitialDialogId = nameof(WaterfallDialog);
        AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[]
        {
            BeginFormflowAsync,
            ProcessRequestAsync,
        }));
        AddDialog(FormDialog.FromForm(BuildResetPasswordForm, FormOptions.PromptInStart));
    }
    
  3. StartAsync をウォーターフォールの最初のステップに置き換えることができます。We can replace StartAsync with the first step of our waterfall. ここでは、コンストラクター内で FormFlow を既に作成しました。We already created the Formflow in the constructor. それ以外は、v3 の呼び出しから v4 の対応する呼び出しへの変換だけを行い、同じロジックを維持します。Otherwise, we're keeping the same logic, just translating the v3 calls to their v4 equivalents.

    private async Task<DialogTurnResult> BeginFormflowAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        await stepContext.Context.SendActivityAsync("Alright I will help you create a temp password.");
    
        // Check the passcode and fail out or begin the Formflow dialog.
        if (SendPassCode(stepContext))
        {
            return await stepContext.BeginDialogAsync(
                nameof(ResetPasswordPrompt),
                cancellationToken: cancellationToken);
        }
        else
        {
            //here we can simply fail the current dialog because we have root dialog handling all exceptions
            throw new Exception("Failed to send SMS. Make sure email & phone number has been added to database.");
        }
    }
    
  4. sendPassCode は主に演習として残してあります。sendPassCode is left mainly as an exercise. 元のコードはコメント アウトされています。このメソッドは true を返すだけです。The original code is commented out, and the method just returns true. また、メール アドレスは、元のボットで使用されていなかったので削除することもできます。Also, we can remove the email address again, as it wasn't used in the original bot.

    private bool SendPassCode(DialogContext context)
    {
        //bool result = false;
    
        //Recipient Id varies depending on channel
        //refer ChannelAccount class https://docs.botframework.com/en-us/csharp/builder/sdkreference/dd/def/class_microsoft_1_1_bot_1_1_connector_1_1_channel_account.html#a0b89cf01fdd73cbc00a524dce9e2ad1a
        //as well as Activity class https://docs.botframework.com/en-us/csharp/builder/sdkreference/dc/d2f/class_microsoft_1_1_bot_1_1_connector_1_1_activity.html
        //int passcode = new Random().Next(1000, 9999);
        //Int64? smsNumber = 0;
        //string smsMessage = "Your Contoso Pass Code is ";
        //string countryDialPrefix = "+1";
    
        // TODO: save PassCode to database
        //using (var db = new ContosoHelpdeskContext())
        //{
        //    var reset = db.ResetPasswords.Where(r => r.EmailAddress == email).ToList();
        //    if (reset.Count >= 1)
        //    {
        //        reset.First().PassCode = passcode;
        //        smsNumber = reset.First().MobileNumber;
        //        result = true;
        //    }
    
        //    db.SaveChanges();
        //}
    
        // TODO: send passcode to user via SMS.
        //if (result)
        //{
        //    result = Helper.SendSms($"{countryDialPrefix}{smsNumber.ToString()}", $"{smsMessage} {passcode}");
        //}
    
        //return result;
        return true;
    }
    
  5. BuildResetPasswordForm に変更はありません。BuildResetPasswordForm has no changes.

  6. ResumeAfterResetPasswordFormDialog をウォーターフォールの 2 番目のステップに置き換えることができます。戻り値はステップ コンテキストから取得します。We can replace ResumeAfterResetPasswordFormDialog with the second step of our waterfall, and we'll get the return value from the step context. 元のダイアログで何にも使用されていなかった電子メール アドレスを削除しました。また、データベースに照会する代わりにダミーの結果を用意しました。We've removed the email address that the original dialog didn't do anything with, and we've provided a dummy result instead of querying the database. v3 の呼び出しから v4 の対応する呼び出しへの変換だけを行い、同じロジックを維持します。We're keeping the same logic, just translating the v3 calls to their v4 equivalents.

    private async Task<DialogTurnResult> ProcessRequestAsync(
        WaterfallStepContext stepContext,
        CancellationToken cancellationToken = default(CancellationToken))
    {
        // Get the result from the Formflow dialog when it ends.
        if (stepContext.Reason != DialogReason.CancelCalled)
        {
            var prompt = stepContext.Result as ResetPasswordPrompt;
            int? passcode;
    
            // TODO: Retrieve the passcode from the database.
            passcode = 1111;
    
            if (prompt.PassCode == passcode)
            {
                string temppwd = "TempPwd" + new Random().Next(0, 5000);
                await stepContext.Context.SendActivityAsync(
                    $"Your temp password is {temppwd}",
                    cancellationToken: cancellationToken);
            }
        }
    
        return await stepContext.EndDialogAsync(null, cancellationToken);
    }
    

必要に応じてモデルをコピーして更新するCopy over and update models as necessary

v4 の FormFlow コミュニティ ライブラリと同じ v3 モデルを使用できます。You can use the same v3 models with the v4 community form flow library.

  1. 自分のプロジェクトで Models フォルダーを作成します。Create a Models folder in your project.
  2. v3 のプロジェクトのモデル ディレクトリからこれらのファイルを新しいモデル ディレクトリにコピーします。Copy these files from the v3 project's models directory into your new models directory.
    • InstallApp.csInstallApp.cs
    • LocalAdmin.csLocalAdmin.cs
    • LocalAdminPrompt.csLocalAdminPrompt.cs
    • ResetPassword.csResetPassword.cs
    • ResetPasswordPrompt.csResetPasswordPrompt.cs

using ステートメントを更新するUpdate using statements

次に示すように、モデル クラスで using ステートメントを更新する必要があります。We need to update using statements in the model classes as shown next.

  1. InstallApps.cs で、次のように変更します。In InstallApps.cs change them to this:

    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
  2. LocalAdmin.cs で、次のように変更します。In LocalAdmin.cs change them to this:

    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    
  3. LocalAdminPrompt.cs で、次のように変更します。In LocalAdminPrompt.cs change them to this:

    using Bot.Builder.Community.Dialogs.FormFlow;
    
  4. ResetPassword.cs で、次のように変更します。In ResetPassword.cs change them to this:

    using System.ComponentModel.DataAnnotations;
    using System.ComponentModel.DataAnnotations.Schema;
    

    また、名前空間内の using ステートメントを削除します。Also, delete the using statements inside the namespace.

  5. ResetPasswordPrompt.cs で、次のように変更します。In ResetPasswordPrompt.cs change them to this:

    using Bot.Builder.Community.Dialogs.FormFlow;
    using System;
    

追加の変更Additional changes

ResetPassword.cs で、次のように MobileNumber の戻り値の型を変更します。In ResetPassword.cs change the return type of the MobileNumber as follows:

public long? MobileNumber { get; set; }

最後の移行手順Final porting steps

移行プロセスを完了するには、次の手順を行います。To complete the porting process, perform these steps:

  1. AdapterWithErrorHandler クラスを作成して、ミドルウェアまたはアプリケーションで例外をキャッチできるエラー ハンドラーを含む、アダプターを定義します。Create an AdapterWithErrorHandler class to define an adapter which includes an error handler that can catch exceptions in the middleware or application. アダプターは、bot ミドルウェアパイプラインを通じて、の受信アクティビティをボットのロジックに処理して、再度送信します。The adapter processes and directs incoming activities in through the bot middleware pipeline to your bot's logic and then back out again. 次のコードを使用して、クラスを作成します。Use the following code to create the class:
using Microsoft.Bot.Builder;
using Microsoft.Bot.Builder.Integration.AspNet.Core;
using Microsoft.Bot.Connector.Authentication;
using System;

namespace ContosoHelpdeskChatBot
{
    public class AdapterWithErrorHandler : BotFrameworkHttpAdapter
    {
        private static log4net.ILog logger
            = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        public AdapterWithErrorHandler(
            ICredentialProvider credentialProvider,
            ConversationState conversationState = null)
            : base(credentialProvider)
        {
            OnTurnError = async (turnContext, exception) =>
            {
                // Log any leaked exception from the application.
                logger.Error($"Exception caught : {exception.Message}");

                // Send a catch-all apology to the user.
                await turnContext.SendActivityAsync("Sorry, it looks like something went wrong.");

                if (conversationState != null)
                {
                    try
                    {
                        // Delete the conversationState for the current conversation to prevent the
                        // bot from getting stuck in a error-loop caused by being in a bad state.
                        // ConversationState should be thought of as similar to "cookie-state" in a Web pages.
                        await conversationState.DeleteAsync(turnContext);
                    }
                    catch (Exception e)
                    {
                        logger.Error($"Exception caught on attempting to Delete ConversationState : {e.Message}");
                    }
                }
            };
        }
    }
}
  1. 好みに合わせて、wwwroot\default.htm ページを変更します。Modify the wwwroot\default.htm page as you see fit.

ボットを実行してエミュレーターでテストするRun and test your bot in the Emulator

この時点で、ボットをローカルの IIS で実行し、エミュレーターを使用してボットに接続できます。At this point, we should be able to run the bot locally in IIS and attach to it with the Emulator.

  1. IIS でボットを実行します。Run the bot in IIS.
  2. エミュレーターを起動し、bot のエンドポイント (たとえば、) に接続し http://localhost:3978/api/messages ます。Start the Emulator and connect to the bot's endpoint (for example, http://localhost:3978/api/messages).
    • 初めてボットを実行する場合は、 [File](ファイル) > [New Bot](新しいボット) をクリックして、画面の指示に従います。If this is the first time you are running the bot then click File > New Bot and follow the instructions on screen. それ以外の場合は、 [File](ファイル) > [Open Bot](ボットを開く) をクリックして既存のボットを開きます。Otherwise, click File > Open Bot to open an existing bot.
    • 構成のポート設定を再確認します。Double check your port settings in the configuration. たとえば、ブラウザーで http://localhost:3979/ にアクセスしてボットを開いている場合は、エミュレーターでボットのエンドポイントを http://localhost:3979/api/messages に設定します。For example, if the bot opened in your browser to http://localhost:3979/, then in the Emulator, set the bot's endpoint to http://localhost:3979/api/messages.
  3. 4 つのダイアログはすべて機能するはずです。ウォーターフォール ステップにブレークポイントを設定して、これらのポイントでダイアログ コンテキストとダイアログの状態を確認できます。All four dialogs should work, and you can set breakpoints in the waterfall steps to check what the dialog context and dialog state is at these points.

その他のリソースAdditional resources

v4 の概念トピックv4 conceptual topics:

v4 の使用方法のトピックv4 how-to topics: