Migrieren eines .NET-Bots der Version 3 zu einem .NET Framework-Bot der Version 4Migrate a .NET v3 bot to a .NET Framework v4 bot

gilt für: SDK v4APPLIES TO: SDK v4

In diesem Artikel konvertieren Sie ContosoHelpdeskChatBot Version 3 in einen Bot der Version 4, ohne dabei den Projekttyp zu konvertieren.In this article we'll convert the v3 ContosoHelpdeskChatBot into a v4 bot without converting the project type. Er bleibt ein .NET Framework-Projekt.It will remain a .NET Framework project. Die Konvertierung ist in folgende Schritte unterteilt:This conversion is broken down into these steps:

  1. Aktualisieren und Installieren von NuGet-PaketenUpdate and install NuGet packages
  2. Aktualisieren der Datei „Global.asax.cs“Update your Global.asax.cs file
  3. Aktualisieren der MessagesController-KlasseUpdate your MessagesController class
  4. Konvertieren der DialogeConvert your dialogs

Das Ergebnis dieser Konvertierung ist der .NET Framework v4 ContosoHelpdeskChatBot.The result of this conversion is the .NET Framework v4 ContosoHelpdeskChatBot. Informationen dazu, wie Sie in einem neuen Projekt zu einem .NET Core-Bot der Version 4 migrieren, finden Sie unter Migrieren eines .NET-Bots der Version 3 zu einem .NET Core-Bot der Version 4.To migrate to a .NET Core v4 bot in new project, see Migrate a .NET v3 bot to a .NET Core v4 bot.

Bot Framework SDK v4 basiert auf der gleichen zugrunde liegenden REST-API wie SDK v3.Bot Framework SDK v4 is based on the same underlying REST API as SDK v3. In SDK v4 wurde die vorherige SDK-Version jedoch umgestaltet, um Entwicklern mehr Flexibilität und Kontrolle über ihre Bots zu bieten.However, SDK v4 is a refactoring of the previous version of the SDK to allow developers more flexibility and control over their bots. Dazu wurden unter anderem folgende wichtige Änderungen an dem SDK vorgenommen:Major changes in the SDK include:

  • Der Zustand wird über Zustandsverwaltungsobjekte und Eigenschaftenaccessoren verwaltet.State is managed via state management objects and property accessors.
  • Das Einrichten von Turn-Handlern und Übergeben von Aktivitäten an den Handler wurden geändert.Setting up turn handler and passing activities to it has changed.
  • Es gibt keine bewertbaren Elemente (Scoreables) mehr.Scorables no longer exist. Sie können überprüfen, ob der Turn-Handler „globale“ Befehle enthält, bevor Sie die Steuerung an Ihre Dialoge übergeben.You can check for "global" commands in the turn handler, before passing control to your dialogs.
  • Eine neue Dialogbibliothek ist verfügbar, die sich sehr von der in der vorherigen Version unterscheidet.A new Dialogs library that is very different from the one in the previous version. Sie müssen alte Dialoge in das neue Dialogsystem mit Komponenten- und Wasserfalldialogen und der Communityimplementierung von FormFlow-Dialogen für v4 konvertieren.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.

Weitere Informationen zu spezifischen Änderungen finden Sie unter Differences between the v3 and v4 .NET SDK (Unterschiede zwischen Version 3 und Version 4 des .NET SDK).For more information about specific changes, see differences between the v3 and v4 .NET SDK.

Aktualisieren und Installieren von NuGet-PaketenUpdate and install NuGet packages

  1. Aktualisieren Sie Microsoft.Bot.Builder.Azure und Microsoft.Bot.Builder.Integration.AspNet.WebApi auf die letzte stabile Version.Update Microsoft.Bot.Builder.Azure and Microsoft.Bot.Builder.Integration.AspNet.WebApi to the latest stable version.

    Da die Pakete Microsoft.Bot.Builder und Microsoft.Bot.Connector Abhängigkeiten sind, werden sie dadurch ebenfalls aktualisiert.This will update the Microsoft.Bot.Builder and Microsoft.Bot.Connector packages as well, since they are dependencies.

  2. Löschen Sie das Paket Microsoft.Bot.Builder.History.Delete the Microsoft.Bot.Builder.History package. Dieses Paket ist nicht Teil des v4-SDKs.This is not part of the v4 SDK.

  3. Fügen Sie das Paket Autofac.WebApi2 hinzu.Add Autofac.WebApi2

    Dieses Paket wird für die Abhängigkeitsinjektion in ASP.NET verwendet.We'll use this to help with dependency injection in ASP.NET.

  4. Fügen Sie Bot.Builder.Community.Dialogs.Formflow hinzu.Add Bot.Builder.Community.Dialogs.Formflow

    Dies ist eine Communitybibliothek zum Erstellen von v4-Dialogen aus v3-FormFlow-Definitionsdateien.This is a community library for building v4 dialogs from v3 Formflow definition files. Microsoft.Bot.Builder.Dialogs ist eine der Abhängigkeiten dieser Bibliothek und wird daher auch installiert.It has Microsoft.Bot.Builder.Dialogs as one of its dependencies, so this is also installed for us.

Tipp

Wenn Ihr Projekt auf.NET Framework 4.6 abzielt, müssen Sie es auf 4.6.1 oder höher aktualisieren, da Bot.Builder.Community.Dialogs.Formflow eine.NET Standard 2.0-Bibliothek ist.If your project targets .NET Framework 4.6, you need to update it to 4.6.1 or later because Bot.Builder.Community.Dialogs.Formflow is a .NET Standard 2.0 library. Weitere Informationen finden Sie unter Unterstützung der .NET-Implementierung.For more information, see .NET implementation support.

Wenn Sie die Erstellung an diesem Punkt vornehmen, erhalten Sie Compilerfehler.If you build at this point, you will get compiler errors. Sie können diese Fehler ignorieren.You can ignore these. Sobald die Konvertierung abgeschlossen ist, verfügen Sie über den funktionierenden Code.Once we're finished with our conversion, we'll have working code.

Aktualisieren der Datei „Global.asax.cs“Update your Global.asax.cs file

Der Gerüstbau wurde in v4 teilweise geändert, und Sie müssen Teile der Infrastruktur für die Zustandsverwaltung selbst einrichten.Some of the scaffolding has changed, and we have to set up parts of the state management infrastructure ourselves in v4. Beispielsweise werden die Authentifizierung und Weiterleitungsaktivitäten für Ihren Botcode in v4 von einem Botadapter behandelt, und die Zustandseigenschaften müssen vorab deklariert werden.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.

Sie erstellen eine Zustandseigenschaft für DialogState, da diese jetzt zur Unterstützung von Dialogen in v4 erforderlich ist.We'll create a state property for DialogState, which we now need for dialog support in v4. Mithilfe der Abhängigkeitsinjektion fügen Sie erforderliche Informationen in den Controller- und Botcode ein.We'll use dependency injection to get necessary information to the controller and bot code.

In Global.asax.cs:In Global.asax.cs:

  1. Aktualisieren Sie die using-Anweisungen:Update the using statements:

    using Autofac;
    using Autofac.Integration.WebApi;
    using ContosoHelpdeskChatBot.Bots;
    using ContosoHelpdeskChatBot.Dialogs;
    using Microsoft.Bot.Builder;
    using Microsoft.Bot.Builder.BotFramework;
    using Microsoft.Bot.Builder.Integration.AspNet.WebApi;
    using Microsoft.Bot.Connector.Authentication;
    using System.Reflection;
    using System.Web.Http;
    
  2. Entfernen Sie diese Zeilen aus der Application_Start-Methode:Remove these lines from the Application_Start method:

    BotConfig.UpdateConversationContainer();
    this.RegisterBotModules();
    

    Fügen Sie diese Zeile ein:And, insert this line:

    GlobalConfiguration.Configure(BotConfig.Register);
    
  3. Entfernen Sie die RegisterBotModules-Methode, auf die nicht mehr verwiesen wird.Remove the RegisterBotModules method, which is no longer referenced.

  4. Ersetzen Sie die BotConfig.UpdateConversationContainer-Methode durch diese BotConfig.Register-Methode, bei der wir Objekte registrieren, die wir zur Unterstützung der Abhängigkeitsinjektion benötigen.Replace the BotConfig.UpdateConversationContainer method with this BotConfig.Register method, where we'll register objects we need to support dependency injection. Für diesen Bot wird weder der Zustand user noch private conversation verwendet, sodass wir nur das Objekt für die Verwaltung des Konversationszustands erstellen.This bot uses neither user nor private conversation state, so we create only the conversation state management object.

    public static void Register(HttpConfiguration config)
    {
        var builder = new ContainerBuilder();
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
    
        // The ConfigurationCredentialProvider will retrieve the MicrosoftAppId and
        // MicrosoftAppPassword from Web.config
        builder.RegisterType<ConfigurationCredentialProvider>().As<ICredentialProvider>().SingleInstance();
    
        // Create the Bot Framework Adapter with error handling enabled.
        builder.RegisterType<AdapterWithErrorHandler>().As<IBotFrameworkHttpAdapter>().SingleInstance();
    
        // The Memory Storage used here is for local bot debugging only. When the bot
        // is restarted, everything stored in memory will be gone.
        IStorage dataStore = new MemoryStorage();
    
        // Create Conversation State object.
        // The Conversation State object is where we persist anything at the conversation-scope.
        var conversationState = new ConversationState(dataStore);
        builder.RegisterInstance(conversationState).As<ConversationState>().SingleInstance();
    
        // Register the main dialog, which is injected into the DialogBot class
        builder.RegisterType<RootDialog>().SingleInstance();
    
        // Register the DialogBot with RootDialog as the IBot interface
        builder.RegisterType<DialogBot<RootDialog>>().As<IBot>();
    
        var container = builder.Build();
        var resolver = new AutofacWebApiDependencyResolver(container);
        config.DependencyResolver = resolver;
    }
    

Aktualisieren der MessagesController-KlasseUpdate your MessagesController class

Da der Bot in v4 hier einen Turn startet, ist eine größere Änderung erforderlich.This is where the bot starts a turn in v4, so this need to change a lot. Mit Ausnahme des Turn-Handlers des Bots selbst kann dieser Teil weitestgehend als ein Baustein betrachtet werden.Except for the bot's turn handler itself, most of this can be thought of as boilerplate. In der Datei Controllers\MessagesController.cs:In your Controllers\MessagesController.cs file:

  1. Aktualisieren Sie die using-Anweisungen:Update the using statements:

    using Microsoft.Bot.Builder;
    using Microsoft.Bot.Builder.Integration.AspNet.WebApi;
    using System.Net.Http;
    using System.Threading.Tasks;
    using System.Web.Http;
    
  2. Entfernen Sie das [BotAuthentication]-Attribut aus der Klasse.Remove the [BotAuthentication] attribute from the class. In v4 wird die Authentifizierung vom Adapter des Bots durchgeführt.In v4, the bot's adapter will handle authentication.

  3. Fügen Sie diese Felder und einen Konstruktor hinzu, um sie zu initialisieren.Add these fields and a constructor to initialize them. Für ASP.NET und Autofac wird die Abhängigkeitsinjektion verwendet, um die Parameterwerte abzurufen.ASP.NET and Autofac use dependency injection to get the parameters values. (Wir haben Adapter- und Bot-Objekte in Global.asax.cs registriert, um dies zu unterstützen.)(We registered adapter and bot objects in Global.asax.cs to support this.)

    private readonly IBotFrameworkHttpAdapter _adapter;
    private readonly IBot _bot;
    
    public MessagesController(IBotFrameworkHttpAdapter adapter, IBot bot)
    {
        _adapter = adapter;
        _bot = bot;
    }
    
  4. Ersetzen Sie den Text der Post-Methode.Replace the body of the Post method. Wir verwenden den Adapter, um die Nachrichtenschleife (Turn-Handler) unseres Bots aufzurufen.We use the adapter to call our bot's message loop (turn handler).

    public async Task<HttpResponseMessage> Post()
    {
        var response = new HttpResponseMessage();
    
        // Delegate the processing of the HTTP POST to the adapter.
        // The adapter will invoke the bot.
        await _adapter.ProcessAsync(Request, response, _bot);
        return response;
    }
    

Löschen der CancelScorable- und GlobalMessageHandlersBotModule-KlassenDelete the CancelScorable and GlobalMessageHandlersBotModule classes

Da es in Version 4 keine Scorables gibt und Sie den Turn-Handler so aktualisiert haben, dass er auf eine cancel-Nachricht reagiert, können die Klassen CancelScorable (in Dialogs\CancelScorable.cs) und GlobalMessageHandlersBotModule gelöscht werden.Since scorables don't exist in v4 and we've updated the turn handler to react to a cancel message, we can delete the CancelScorable (in Dialogs\CancelScorable.cs) and GlobalMessageHandlersBotModule classes.

Erstellen Ihrer BotklasseCreate your bot class

In v4 sind der Turn-Handler bzw. die Nachrichtenschleife hauptsächlich in einer Botdatei enthalten.In v4, the turn handler or message loop logic is primarily in a bot file. Wir führen die Ableitung von ActivityHandler durch, womit Handler für gängige Arten von Aktivitäten definiert werden.We're deriving from ActivityHandler, which defines handlers for common types of activities.

  1. Erstellen Sie die Datei Bots\DialogBots.cs.Create a Bots\DialogBots.cs file.

  2. Aktualisieren Sie die using-Anweisungen: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. Leiten Sie DialogBot von ActivityHandler ab, und fügen Sie einen generischen Parameter für den Dialog hinzu.Derive DialogBot from ActivityHandler, and add a generic parameter for the dialog.

    public class DialogBot<T> : ActivityHandler where T : Dialog
    
  4. Fügen Sie diese Felder und einen Konstruktor hinzu, um sie zu initialisieren.Add these fields and a constructor to initialize them. Für ASP.NET und Autofac wird wieder die Abhängigkeitsinjektion verwendet, um die Parameterwerte abzurufen.Again, ASP.NET and Autofac use 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. Setzen Sie OnMessageActivityAsync außer Kraft, um unseren Hauptdialog aufzurufen.Override OnMessageActivityAsync to invoke our main dialog. (Wir definieren die Run-Erweiterungsmethode in Kürze.)(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);
    }
    
  6. Setzen Sie OnTurnAsync außer Kraft, um den Konversationszustand am Ende des Turns zu speichern.Override OnTurnAsync to save our conversation state at the end of the turn. In v4 müssen wir dies explizit durchführen, um den Zustand auf die Persistenzebene zu schreiben.In v4, we have to do this explicitly to write state out to the persistence layer. Mit der ActivityHandler.OnTurnAsync-Methode werden spezifische Aktivitätshandlermethoden basierend auf dem Typ der empfangenen Aktivität aufgerufen, sodass wir den Zustand nach dem Aufruf in der Basismethode speichern.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);
    }
    

Erstellen der Run-ErweiterungsmethodeCreate the Run extension method

Wir erstellen eine Erweiterungsmethode, um den Code zusammenzufassen, der zum Ausführen eines reinen Komponentendialogs über unseren Bot benötigt wird.We're creating an extension method to consolidate the code needed to run a bare component dialog from our bot.

Erstellen Sie die Datei DialogExtensions.cs, und implementieren Sie eine Run-Erweiterungsmethode.Create a DialogExtensions.cs file and implement a Run extension method.

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

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

            var dialogContext = await dialogSet.CreateContextAsync(turnContext, cancellationToken);

            // Handle 'cancel' interruption
            if (turnContext.Activity.Text.Equals("cancel", StringComparison.InvariantCultureIgnoreCase))
            {
                var reply = turnContext.Activity.CreateReply($"Ok restarting conversation.");
                await turnContext.SendActivityAsync(reply);
                await dialogContext.CancelAllDialogsAsync();
            }

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

Konvertieren der DialogeConvert your dialogs

Sie nehmen an den ursprünglichen Dialogen viele Änderungen vor, um sie zum v4-SDK zu migrieren.We'll make many changes to the original dialogs to migrate them to the v4 SDK. Die Compilerfehler können Sie vorerst ignorieren.Don't worry about the compiler errors for now. Diese Fehler werden aufgelöst, wenn die Konvertierung abgeschlossen ist.These will resolve once we've finished the conversion. Da Sie nicht mehr Änderungen als nötig am ursprünglichen Code vornehmen, werden einige Compilerwarnungen auch nach der Migration noch angezeigt.In the interest of not modifying the original code more than necessary, there will remain some compiler warnings after we've finished the migration.

Alle Dialoge werden von ComponentDialog abgeleitet, anstatt die IDialog<object>-Schnittstelle von v3 zu implementieren.All of our dialogs will derive from ComponentDialog, instead of implementing the IDialog<object> interface of v3.

Dieser Bot enthält vier Dialoge, die konvertiert werden müssen:This bot has four dialogs that we need to convert:

DialogDialog BESCHREIBUNGDescription
RootDialogRootDialog Zeigt Optionen an und startet die anderen Dialoge.Presents options and starts the other dialogs.
InstallAppDialogInstallAppDialog Verarbeitet Anforderungen zum Installieren einer App auf einem Computer.Handles requests to install an app on a machine.
LocalAdminDialogLocalAdminDialog Verarbeitet Anforderungen für lokale Computeradministratorrechte.Handles requests for local machine admin rights.
ResetPasswordDialogResetPasswordDialog Verarbeitet Anforderungen zum Zurücksetzen Ihres Kennworts.Handles requests to reset your password.

Diese Dialoge erfassen Eingaben, führen aber keinen der genannten Vorgänge auf Ihrem Computer aus.These gather input, but do not perform any of these operations on your machine.

Projektmappenweite Änderungen der DialogeMake solution-wide dialog changes

  1. Ersetzen Sie für die gesamte Projektmappe alle Vorkommen von IDialog<object> durch ComponentDialog.For the entire solution, Replace all occurrences of IDialog<object> with ComponentDialog.
  2. Ersetzen Sie für die gesamte Projektmappe alle Vorkommen von IDialogContext durch DialogContext.For the entire solution, Replace all occurrences of IDialogContext with DialogContext.
  3. Entfernen Sie für jede Dialogklasse das [Serializable]-Attribut.For each dialog class, remove the [Serializable] attribute.

Die Ablaufsteuerung und das Messaging in Dialogen werden nicht mehr auf die gleiche Weise behandelt. Diese Komponenten müssen daher beim Konvertieren der einzelnen Dialoge überarbeitet werden.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.

VorgangOperation v3-Codev3 code v4-Codev4 code
Start des Dialogs behandelnHandle the start of your dialog Implementieren Sie IDialog.StartAsync.Implement IDialog.StartAsync Legen Sie diesen Vorgang als ersten Schritt eines Wasserfalldialogs fest, oder implementieren Sie Dialog.BeginDialogAsync.Make this the first step of a waterfall dialog, or implement Dialog.BeginDialogAsync
Fortsetzung des Dialogs behandelnHandle continuation of your dialog Rufen Sie IDialogContext.Wait auf.Call IDialogContext.Wait Fügen Sie einem Wasserfalldialog zusätzliche Schritte hinzu, oder implementieren Sie Dialog.ContinueDialogAsync.Add additional steps to a waterfall dialog, or implement Dialog.ContinueDialogAsync
Nachricht an den Benutzer sendenSend a message to the user Rufen Sie IDialogContext.PostAsync auf.Call IDialogContext.PostAsync Rufen Sie ITurnContext.SendActivityAsync auf.Call ITurnContext.SendActivityAsync
Untergeordneten Dialog startenStart a child dialog Rufen Sie IDialogContext.Call auf.Call IDialogContext.Call Rufen Sie DialogContext.BeginDialogAsync auf.Call DialogContext.BeginDialogAsync
Abschluss des aktuellen Dialogs signalisierenSignal that the current dialog has completed Rufen Sie IDialogContext.Done auf.Call IDialogContext.Done Rufen Sie DialogContext.EndDialogAsync auf.Call DialogContext.EndDialogAsync
Eingabe des Benutzers abrufenGet the user's input Verwenden Sie einen IAwaitable<IMessageActivity>-Parameter.Use an IAwaitable<IMessageActivity> parameter Verwenden Sie eine Eingabeaufforderung in einem Wasserfall oder ITurnContext.Activity.Use a prompt from within a waterfall, or use ITurnContext.Activity

Hinweise zum v4-Code:Notes about the v4 code:

  • Verwenden Sie im Dialogcode die DialogContext.Context-Eigenschaft, um den aktuellen Turn-Kontext zu erhalten.Within dialog code, use the DialogContext.Context property to get the current turn context.
  • Wasserfallschritte verfügen über einen von DialogContext abgeleiteten Parameter WaterfallStepContext.Waterfall steps have a WaterfallStepContext parameter, which derives from DialogContext.
  • Alle konkreten Dialog- und Eingabeaufforderungsklassen werden von der abstrakten Dialog-Klasse abgeleitet.All concrete dialog and prompt classes derive from the abstract Dialog class.
  • Sie weisen eine ID zu, wenn Sie einen Komponentendialog erstellen.You assign an ID when you create a component dialog. Jedem Dialog in einem Dialogsatz muss eine ID zugewiesen sein, die innerhalb dieses Satzes eindeutig ist.Each dialog in a dialog set needs to be assigned an ID unique within that set.

Aktualisieren des StammdialogsUpdate the root dialog

Der Stammdialog in diesem Bot fordert den Benutzer auf, eine Auswahl aus einer Reihe von Optionen zu treffen, und startet dann basierend auf dieser Auswahl einen untergeordneten 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. Diese Schritte werden anschließend für die Lebensdauer der Konversation in einer Schleife wiederholt.This then loops for the lifetime of the conversation.

  • Der Hauptflow kann als Wasserfalldialog eingerichtet werden. Dies ist ein neues Konzept im v4-SDK.We can set the main flow up as a waterfall dialog, which is a new concept in the v4 SDK. Der Wasserfalldialog durchläuft nacheinander einen festen Satz von Schritten.It will run through a fixed set of steps in order. Weitere Informationen finden Sie unter Implementieren eines sequenziellen Konversationsflusses.For more information, see Implement sequential conversation flow.
  • Eingabeaufforderungen werden nun über Eingabeaufforderungsklassen gesteuert. Hierbei handelt es sich um kurze untergeordnete Dialoge, die zu einer Eingabe auffordern, eine minimale Verarbeitung und Validierung durchführen und einen Wert zurückgeben.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. Weitere Informationen finden Sie unter Erfassen von Benutzereingaben mit einer Dialogaufforderung.For more information, see gather user input using a dialog prompt.

In der Datei Dialogs/RootDialog.cs:In the Dialogs/RootDialog.cs file:

  1. Aktualisieren Sie die using-Anweisungen: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. Die Optionen für HelpdeskOptions müssen von einer Liste mit Zeichenfolgen in eine Liste mit Optionen konvertiert werden.We need to convert HelpdeskOptions options from a list of strings to a list of choices. Diese Liste wird mit einer Auswahleingabeaufforderung verwendet, die die Nummer der Auswahl (in der Liste), den Wert der Auswahl oder eines der Synonyme der Auswahl als gültige Eingabe akzeptiert.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. Fügen Sie einen Konstruktor hinzu.Add a constructor. Im Code werden folgende Schritte ausgeführt:This code does the following:

    • Jeder Instanz eines Dialogs wird bei ihrer Erstellung eine ID zugewiesen.Each instance of a dialog is assigned an ID when it is created. Die Dialog-ID ist Teil des Dialogsatzes, dem der Dialog hinzugefügt wird.The dialog ID is part of the dialog set to which the dialog is being added. Sie wissen bereits, dass der Bot mit einem Dialogobjekt in der MessageController-Klasse initialisiert wurde.Recall that the bot was initialized with a dialog object in the MessageController class. Jeder ComponentDialog verfügt über einen eigenen internen Dialogsatz mit eigenen Dialog-IDs.Each ComponentDialog has its own internal dialog set, with its own set of dialog IDs.
    • Die anderen Dialoge (einschließlich der Auswahleingabeaufforderung) werden als untergeordnete Dialoge hinzugefügt.It adds the other dialogs, including the choice prompt, as child dialogs. Hier verwenden Sie nur den Klassennamen für jede Dialog-ID.Here, we're just using the class name for each dialog ID.
    • Ein Wasserfalldialog mit drei Schritten wird definiert.It defines a three-step waterfall dialog. Diese Schritte werden Sie gleich implementieren.We'll implement those in a moment.
      • Der Dialog fordert den Benutzer zunächst auf, eine Aufgabe zur Ausführung auszuwählen.The dialog will first prompt the user to choose a task to perform.
      • Anschließend startet er den untergeordneten Dialog, der dieser Auswahl zugeordnet ist.Then, start the child dialog associated with that choice.
      • Zum Schluss startet er sich selbst neu.And finally, restart itself.
    • Jeder Schritt des Wasserfalls ist ein Delegat. Diese Delegate implementieren Sie als Nächstes, wobei Sie soweit möglich vorhandenen Code aus dem ursprünglichen Dialog nutzen.Each step of the waterfall is a delegate, and we'll implement those next, taking existing code from the original dialog where we can.
    • Wenn Sie einen Komponentendialog starten, wird sein erster Dialog gestartet.When you start a component dialog, it will start its initial dialog. Standardmäßig ist dies der erste untergeordnete Dialog, der einem Komponentendialog hinzugefügt wird.By default, this is the first child dialog added to a component dialog. Wir legen hier explizit die InitialDialogId-Eigenschaft fest. Der Hauptwasserfalldialog muss dem Satz daher nicht als Erstes hinzugefügt werden.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. Wenn Sie beispielsweise zuerst die Eingabeaufforderungen hinzufügen möchten, ist dies möglich, ohne ein Laufzeitproblem zu verursachen.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. Wir können die StartAsync-Methode löschen.We can delete the StartAsync method. Wenn ein Komponentendialog gestartet wird, startet er automatisch seinen ersten Dialog.When a component dialog begins, it automatically starts its initial dialog. In diesem Fall ist dies der Wasserfalldialog, den Sie im Konstruktor definiert haben.In this case, that's the waterfall dialog we defined in the constructor. Der Wasserfalldialog wird ebenfalls automatisch mit seinem ersten Schritt gestartet.That also automatically starts at its first step.

  5. Sie löschen die Methoden MessageReceivedAsync und ShowOptions und ersetzen sie durch den ersten Schritt des Wasserfalls.We will delete the MessageReceivedAsync and ShowOptions methods, and replace them with the first step of our waterfall. Mit diesen beiden Methoden wird der Benutzer begrüßt und aufgefordert, eine der verfügbaren Optionen auszuwählen.These two methods greeted the user and asked them to choose one of the available options.

    • Wie Sie sehen, werden die Auswahlliste sowie die Begrüßungs- und Fehlermeldungen als Optionen im Aufruf der Auswahleingabeaufforderung bereitgestellt.Here you can see the choice list and the greeting and error messages are provided as options in the call to our choice prompt.
    • Die als Nächstes aufzurufende Methode muss nicht im Dialog angegeben werden, da der Wasserfall nach Abschluss der Auswahleingabeaufforderung mit dem nächsten Schritt fortgesetzt wird.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.
    • Die Auswahleingabeaufforderung wird als Schleife ausgeführt, bis sie eine gültige Eingabe empfängt oder der gesamte Dialogstapel abgebrochen wird.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 kann durch den zweiten Schritt des Wasserfalls ersetzt werden.We can replace OnOptionSelected with the second step of our waterfall. Sie starten weiterhin basierend auf der Eingabe des Benutzers einen untergeordneten Dialog.We still start a child dialog based on the user's input.

    • Die Auswahleingabeaufforderung gibt einen FoundChoice-Wert zurück.The choice prompt returns a FoundChoice value. Dieser Wert wird in der Result-Eigenschaft des Schrittkontexts angezeigt.This shows up in the step context's Result property. Der Dialogstapel behandelt alle Rückgabewerte als Objekte.The dialog stack treats all return values as objects. Stammt der Rückgabewert aus einem Ihrer Dialoge, wissen Sie, um welchen Werttyp es sich bei dem Objekt handelt.If the return value is from one of your dialogs, then you know what type of value the object is. Eine Liste der Rückgabewerte der einzelnen Eingabeaufforderungstypen finden Sie unter Eingabeaufforderungstypen.See prompt types for a list what each prompt type returns.
    • Da die Auswahleingabeaufforderung keine Ausnahme auslöst, können wir den try-catch-Block entfernen.Since the choice prompt won't throw an exception, we can remove the try-catch block.
    • Sie müssen eine Methode zum Fortfahren hinzufügen, damit diese Methode immer einen geeigneten Wert zurückgibt.We need to add a fall through so that this method always returns an appropriate value. Dieser Code sollte niemals erreicht werden. Wenn dies jedoch der Fall ist, ermöglicht er das ordnungsgemäße Abbrechen des Dialogs.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. Ersetzen Sie zum Schluss die alte ResumeAfterOptionDialog-Methode durch den letzten Schritt des Wasserfalls.Finally, replace the old ResumeAfterOptionDialog method with the last step of our waterfall.

    • Anstatt wie im ursprünglichen Dialog den Dialog zu beenden und die Ticketnummer zurückzugeben, starten Sie den Wasserfall neu, indem Sie im Stapel die ursprüngliche Instanz durch eine neue Instanz des Wasserfalls ersetzen.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. Dies ist möglich, da die ursprüngliche App den Rückgabewert (die Ticketnummer) immer ignoriert und den Stammdialog neu gestartet hat.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);
    }
    

Aktualisieren des Dialogs zum Installieren der App (InstallAppDialog)Update the install app dialog

Der Dialog zum Installieren der App führt einige logische Aufgaben aus, die Sie als einen Wasserfalldialog mit vier Schritten einrichten.The install app dialog performs a few logical tasks, which we'll set up as a 4-step waterfall dialog. Die Einbeziehung von vorhandenem Code in Wasserfallschritte ist eine logische Aufgabe, die Sie für jeden Dialog ausführen.How you factor existing code into waterfall steps is a logical exercise for each dialog. Für jeden Schritt wird die ursprüngliche Methode notiert, aus der der Code stammt.For each step, the original method the code came from is noted.

  1. Fordert den Benutzer zur Eingabe einer Suchzeichenfolge auf.Asks the user for a search string.
  2. Fragt eine Datenbank ab, um potenzielle Übereinstimmungen zu ermitteln.Queries a database for potential matches.
    • Wenn ein Treffer gefunden wird, wird er ausgewählt, und der Dialog wird fortgesetzt.If there is one hit, select this and continue.
    • Wenn mehrere Treffer gefunden werden, wird der Benutzer aufgefordert, eine Auswahl zu treffen.If there are multiple hits, it asks the user to choose one.
    • Wenn keine Treffer gefunden werden, wird der Dialog beendet.If there are no hits, the dialog exits.
  3. Fordert den Benutzer zur Angabe eines Computers auf, auf dem die App installiert werden soll.Asks the user for a machine to install the app on.
  4. Schreibt die Informationen in eine Datenbank und sendet eine Bestätigungsmeldung.Writes the information to a database and sends a confirmation message.

In der Datei Dialogs/InstallAppDialog.cs:In the Dialogs/InstallAppDialog.cs file:

  1. Aktualisieren Sie die using-Anweisungen: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. Definieren Sie eine Konstante für den Schlüssel, den wir zum Nachverfolgen der gesammelten Informationen verwenden.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. Fügen Sie einen Konstruktor hinzu, und initialisieren Sie den Dialogsatz der Komponente.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 kann durch den ersten Schritt des Wasserfalls ersetzt werden.We can replace StartAsync with the first step of our waterfall.

    • Da Sie den Zustand selbst verwalten müssen, verfolgen Sie das InstallApp-Objekt im Dialogzustand nach.We have to manage state ourselves, so we'll track the install app object in dialog state.
    • Die Nachricht, mit der der Benutzer zur Eingabe aufgefordert wird, wird zu einer Option im Aufruf der Eingabeaufforderung.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. appNameAsync und multipleAppsAsync können durch den zweiten Schritt des Wasserfalls ersetzt werden.We can replace appNameAsync and multipleAppsAsync with the second step of our waterfall.

    • Hier rufen Sie das Ergebnis der Eingabeaufforderung ab, anstatt nur die letzte Nachricht des Benutzers zu überprüfen.We're getting the prompt result now, instead of just looking at the user's last message.
    • Die Datenbankabfrage und if-Anweisungen sind wie in appNameAsync organisiert.The database query and if statements are organized the same as in appNameAsync. Der Code in den einzelnen Blöcken der If-Anweisung wurde aktualisiert, damit er mit v4-Dialogen funktioniert.The code in each block of the if statement has been updated to work with v4 dialogs.
      • Wenn ein Treffer zurückgegeben wird, aktualisieren Sie den Dialogzustand und fahren mit dem nächsten Schritt fort.If we have one hit, we'll update dialog state and continue with the next step.
      • Wenn mehrere Treffer zurückgegeben werden, fordern Sie den Benutzer mit der Auswahleingabeaufforderung auf, eine Option in der Liste auszuwählen.If we have multiple hits, we'll use our choice prompt to ask the user to choose from the list of options. Dies bedeutet, dass multipleAppsAsync einfach gelöscht werden kann.This means we can just delete multipleAppsAsync.
      • Wenn keine Treffer zurückgegeben werden, beenden Sie diesen Dialog und geben NULL an den Stammdialog zurück.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 hat den Benutzer nach dem Auflösen der Abfrage auch zur Eingabe des Computernamens aufgefordert.appNameAsync also asked the user for their machine name after it resolved the query. Diesen Teil der Logik greifen wir im nächsten Schritt des Wasserfalls auf.We'll capture that portion of the logic in the next step of the waterfall.

    • Wie gesagt, müssen Sie den Zustand in v4 selbst verwalten.Again, in v4 we have to manage state ourselves. Schwierig ist hierbei lediglich, dass Sie nur über zwei unterschiedliche Logikverzweigungen im vorherigen Schritt zu diesem Schritt gelangen können.The only tricky thing here is that we can get to this step through two different logic branches in the previous step.
    • Sie fordern den Benutzer mit der gleichen Texteingabeaufforderung wie zuvor zur Eingabe eines Computernamens auf und stellen dabei nur andere Optionen bereit.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. Die Logik aus machineNameAsync wird im letzten Schritt des Wasserfalls umschlossen.The logic from machineNameAsync is wrapped up in the final step of our waterfall.

    • Sie rufen den Computernamen aus dem Ergebnis der Texteingabeaufforderung ab und aktualisieren den Dialogzustand.We retrieve the machine name from the text prompt result and update dialog state.
    • Den Aufruf zum Aktualisieren der Datenbank entfernen Sie, weil der zugrunde liegende Code in einem anderen Projekt enthalten ist.We are removing the call to update the database, as the supporting code is in a different project.
    • Anschließend senden Sie die Erfolgsmeldung an den Benutzer und beenden den Dialog.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. Zum Simulieren des Datenbankaufrufs nutzen wir eine Simulation von getAppsAsync, um anstelle der Datenbank eine statische Liste abzufragen.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",
    };
    

Aktualisieren des Dialogs für den lokalen Administrator (LocalAdminDialog)Update the local admin dialog

In v3 hat dieser Dialog den Benutzer begrüßt, den FormFlow-Dialog gestartet und das Ergebnis dann in einer Datenbank gespeichert.In v3, this dialog greeted the user, started the Formflow dialog, and then saved the result off to a database. Dies lässt sich ganz einfach in einen Wasserfall mit zwei Schritten umwandeln.This translates easily into a two-step waterfall.

  1. Aktualisieren Sie die using-Anweisungen.Update the using statements. Beachten Sie, dass dieser Dialog einen v3-FormFlow-Dialog enthält.Note that this dialog includes a v3 Formflow dialog. In v4 können Sie die FormFlow-Communitybibliothek verwenden.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. Sie können die Instanzeigenschaft für LocalAdmin entfernen, da das Ergebnis im Dialogzustand verfügbar ist.We can remove the instance property for LocalAdmin, as the result will be available in dialog state.

  3. Fügen Sie einen Konstruktor hinzu, und initialisieren Sie den Dialogsatz der Komponente.Add a constructor and initialize the component's dialog set. Der FormFlow-Dialog wird auf die gleiche Weise erstellt.The Formflow dialog is created in the same way. Er wird dem Dialogsatz der Komponente einfach im Konstruktor hinzugefügt.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 kann durch den ersten Schritt des Wasserfalls ersetzt werden.We can replace StartAsync with the first step of our waterfall. Sie haben den FormFlow-Dialog bereits im Konstruktor erstellt, und die anderen beiden Anweisungen werden entsprechend angepasst.We already created the Formflow in the constructor, and the other two statements translate to this. Beachten Sie, dass mit FormBuilder der Typname des Modells als ID des generierten Dialogs zugewiesen wird. Für dieses Modell ist dies 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 kann durch den zweiten Schritt des Wasserfalls ersetzt werden.We can replace ResumeAfterLocalAdminFormDialog with the second step of our waterfall. Der Rückgabewert muss nicht aus einer Instanzeigenschaft, sondern aus dem Schrittkontext abgerufen werden.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. BuildLocalAdminForm bleibt weitgehend unverändert. Der einzige Unterschied besteht darin, dass die Instanzeigenschaft nicht vom FormFlow-Dialog aktualisiert wird.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();
    }
    

Aktualisieren des Dialogs für die Kennwortzurücksetzung (ResetPasswordDialog)Update the reset password dialog

In v3 hat dieser Dialog den Benutzer begrüßt, den Benutzer mit einem Passcode autorisiert, den FormFlow-Dialog fehlschlagen lassen oder gestartet und anschließend das Kennwort zurückgesetzt.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. Auch dies lässt sich gut in einen Wasserfall umwandeln.This still translates well into a waterfall.

  1. Aktualisieren Sie die using-Anweisungen.Update the using statements. Beachten Sie, dass dieser Dialog einen v3-FormFlow-Dialog enthält.Note that this dialog includes a v3 Formflow dialog. In v4 können Sie die FormFlow-Communitybibliothek verwenden.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. Fügen Sie einen Konstruktor hinzu, und initialisieren Sie den Dialogsatz der Komponente.Add a constructor and initialize the component's dialog set. Der FormFlow-Dialog wird auf die gleiche Weise erstellt.The Formflow dialog is created in the same way. Er wird dem Dialogsatz der Komponente einfach im Konstruktor hinzugefügt.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 kann durch den ersten Schritt des Wasserfalls ersetzt werden.We can replace StartAsync with the first step of our waterfall. Sie haben den FormFlow-Dialog bereits im Konstruktor erstellt.We already created the Formflow in the constructor. Ansonsten verwenden Sie die gleiche Logik und übersetzen lediglich die v3-Aufrufe in ihre v4-Entsprechungen.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 wurde in erster Linie zu Übungszwecken beibehalten.sendPassCode is left mainly as an exercise. Der ursprüngliche Code ist auskommentiert, und die Methode gibt einfach „true“ zurück.The original code is commented out, and the method just returns true. Außerdem können Sie die E-Mail-Adresse entfernen, da diese im ursprünglichen Bot nicht verwendet wurde.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 weist keine Änderungen auf.BuildResetPasswordForm has no changes.

  6. Sie können ResumeAfterResetPasswordFormDialog durch den zweiten Schritt des Wasserfalls ersetzen und den Rückgabewert aus dem Schrittkontext abrufen.We can replace ResumeAfterResetPasswordFormDialog with the second step of our waterfall, and we'll get the return value from the step context. Die E-Mail-Adresse, die im ursprünglichen Dialog nicht verwendet wurde, haben Sie entfernt, und anstatt die Datenbank abzufragen, haben Sie ein Dummyergebnis bereitgestellt.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. Sie verwenden die gleiche Logik und übersetzen lediglich die v3-Aufrufe in ihre v4-Entsprechungen.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);
    }
    

Aktualisieren der ModelleUpdate models as necessary

In einigen der Modelle, die auf die FormFlow-Bibliothek verweisen, müssen Sie using-Anweisungen aktualisieren.We need to update using statements in some of the models that reference the Formflow library.

  1. Ändern Sie die Anweisungen in LocalAdminPrompt wie folgt:In LocalAdminPrompt, change them to this:

    using Bot.Builder.Community.Dialogs.FormFlow;
    
  2. Ändern Sie die Anweisungen in ResetPasswordPrompt wie folgt:In ResetPasswordPrompt, change them to this:

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

Aktualisieren der Datei „Web.config“Update Web.config

Kommentieren Sie die Konfigurationsschlüssel für MicrosoftAppId und MicrosoftAppPassword aus.Comment out the configuration keys for MicrosoftAppId and MicrosoftAppPassword. Dadurch können Sie den Bot lokal debuggen, ohne diese Werte im Emulator anzugeben.This will allow you to debug the bot locally without needing to provide these values to the Emulator.

Ausführen und Testen des Bots im EmulatorRun and test your bot in the Emulator

An diesem Punkt sollten Sie in der Lage sein, den Bot lokal in IIS auszuführen und Anfügungen mit dem Emulator vorzunehmen.At this point, we should be able to run the bot locally in IIS and attach to it with the Emulator.

  1. Führen Sie den Bot in IIS aus.Run the bot in IIS.
  2. Starten Sie den Emulator, und stellen Sie eine Verbindung mit dem Endpunkt des Bots her (z. http://localhost:3978/api/messages B. ).Start the Emulator and connect to the bot's endpoint (for example, http://localhost:3978/api/messages).
    • Wenn Sie den Bot zum ersten Mal ausführen, klicken Sie auf Datei > Neuer Bot, und folgen Sie den Anweisungen auf dem Bildschirm.If this is the first time you are running the bot then click File > New Bot and follow the instructions on screen. Andernfalls klicken Sie auf Datei > Bot öffnen, um einen vorhandenen Bot zu öffnen.Otherwise, click File > Open Bot to open an existing bot.
    • Überprüfen Sie die Porteinstellungen in der Konfiguration.Double check your port settings in the configuration. Wenn der Bot im Browser beispielsweise mit http://localhost:3979/ geöffnet wurde, legen Sie im Emulator den Endpunkt des Bots auf http://localhost:3979/api/messages fest.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. Alle vier Dialoge sollten funktionieren. Zudem können Sie Breakpoints in den Wasserfallschritten festlegen, um den Dialogzustand und Dialogkontext an diesen Punkten zu überprüfen.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.

Zusätzliche RessourcenAdditional resources

Themen zu v4-Konzepten:v4 conceptual topics:

Schrittanleitungen für v4:v4 how-to topics: