장기 실행 작업 관리Manage a long-running operation

적용 대상: SDK v4APPLIES TO: SDK v4

장기 실행 작업의 적절한 처리는 강력한 봇의 중요한 측면입니다.Proper handling of long-running operations is an important aspect of a robust bot. Azure Bot Service 채널에서 봇으로 활동을 보내면 봇은 작업을 신속하게 처리해야 합니다.When the Azure Bot Service sends an activity to your bot from a channel, the bot is expected to process the activity quickly. 봇이 채널에 따라 10~15초 이내에 작업을 완료하지 않으면 봇 작동 방식에 설명된 대로 Azure Bot Service 시간 초과되고 클라이언트에 다시 504:GatewayTimeout 보고됩니다.If the bot does not complete the operation within 10 to 15 seconds, depending on the channel, the Azure Bot Service will timeout and report back to the client a 504:GatewayTimeout, as described in How bots work.

이 문서에서는 외부 서비스를 사용하여 작업을 실행하고 완료 시 봇에 알리는 방법을 설명합니다.This article describes how to use an external service to execute the operation and to notify the bot when it has completed.

필수 구성 요소Prerequisites

이 샘플 정보About this sample

이 문서는 다중 턴 프롬프트 샘플 봇으로 시작하고 장기 실행 작업을 수행하기 위한 코드를 추가합니다.This article begins with the multi-turn prompt sample bot and adds code for performing a long-running operations. 또한 작업이 완료된 후 사용자에게 응답하는 방법을 보여줍니다.It also demonstrates how to respond to a user after the operation has completed. 업데이트된 샘플에서:In the updated sample:

  • 봇은 사용자에게 수행할 장기 실행 작업을 요청합니다.The bot asks the user which long-running operation to perform.
  • 봇은 사용자로부터 활동을 수신하고 수행할 작업을 결정합니다.The bot receives an activity from the user, and determines which operation to perform.
  • 봇은 사용자에게 작업이 다소 시간이 소요될 것임을 알리고 작업을 C# 함수로 보냅니다.The bot notifies the user the operation will take some time and sends the operation off to a C# function.
    • 봇은 진행 중인 작업이 있음을 나타내는 상태를 저장합니다.The bot saves state, indicating there is an operation in progress.
    • 작업이 실행되는 동안 봇은 사용자의 메시지에 응답하여 작업이 아직 진행 중임을 알립니다.While the operation is running, the bot responds to messages from the user, notifying them the operation is still in progress.
    • Azure Functions 장기 실행 작업을 관리하고 event 작업을 봇에 보내 작업이 완료되었음을 알립니다.Azure Functions manages the long-running operation and sends an event activity to the bot, notifying it that the operation completed.
  • 봇은 대화를 다시 시작하고 사용자에게 작업이 완료되었음을 알리는 자동 관리 메시지를 보냅니다.The bot resumes the conversation and sends a proactive message to notify the user that the operation completed. 그런 다음, 봇은 앞서 언급한 작업 상태를 지웁니다.The bot then clears the operation state mentioned earlier.

이 예제에서는 LongOperationPrompt 추상 클래스에서 파생된 ActivityPrompt 클래스를 정의합니다.This example defines a LongOperationPrompt class, derived from the abstract ActivityPrompt class. 는 처리할 활동을 큐에 대기할 때 활동의 LongOperationPrompt value 속성 내에서 사용자의 선택을 포함합니다.When the LongOperationPrompt queues the activity to be processed, it includes a choice from the user within the activity's value property. 그런 다음, 이 작업은 Direct Line 클라이언트를 사용하여 봇으로 다시 전송되기 전에 Azure Functions 사용, 수정 및 다른 event 활동에 래핑됩니다.This activity is then consumed by Azure Functions, modified, and wrapped in a different event activity before it is sent back to the bot using a Direct Line client. 봇 내에서 이벤트 활동은 어댑터의 대화 계속 메서드를 호출하여 대화를 다시 시작하는 데 사용됩니다.Within the bot, the event activity is used to resume the conversation by calling the adapter's continue conversation method. 그런 다음 대화 스택이 로드되고 가 LongOperationPrompt 완료됩니다.The dialog stack is then loaded, and the LongOperationPrompt completes.

이 문서에서는 다양한 기술을 다룹니다.This article touches on many different technologies. 관련 문서에 대한 링크는 추가 리소스 섹션을 참조하세요.See the additional resources section for links to associated articles.

Azure Storage 계정 만들기Create an Azure Storage account

Azure Storage 계정을 만들고 연결 문자열을 검색합니다.Create an Azure Storage account, and retrieve the connection string. 연결 문자열을 봇의 구성 파일에 추가해야 합니다.You will need to add the connection string to your bot's configuration file.

자세한 내용은 스토리지 계정 만들기 및 Azure Portal 자격 증명 복사를 참조하세요.For more information, see create a storage account and copy your credentials from the Azure portal.

봇 채널 등록 만들기Create a Bot Channels Registration

  1. 등록을 만들기 전에 ngrok를 설치하고 로컬 디버깅 중에 봇의 메시징 엔드포인트로 사용할 URL을 검색합니다.Before creating the registration, setup ngrok and retrieve a URL to be used as the bot's messaging endpoint during local debugging. 메시징 엔드포인트는 가 추가된 HTTPS 전달 /api/messages/ URL이 됩니다.The messaging endpoint will be the HTTPS forwarding URL with /api/messages/ appended. 새 봇의 기본 포트는 3978입니다.Note that the default port for new bots is 3978.

    자세한 내용은 ngrok 를 사용하여 봇을 디버그하는 방법을 참조하세요.For more information, see how to debug a bot using ngrok.

  2. Azure Portal 또는 Azure CLI 사용하여 봇 채널 등록을 만듭니다.Create a Bot Channels Registration in the Azure portal or with the Azure CLI. 봇의 메시징 엔드포인트를 ngrok로 만든 엔드포인트로 설정합니다.Set the bot's messaging endpoint to the one you created with ngrok. 봇 채널 등록 리소스를 만든 후 봇의 Microsoft 앱 ID 및 암호를 얻습니다.After the Bot Channels Registration resource is created, obtain the bot's Microsoft app ID and password. Direct Line 채널을 사용하도록 설정하고 Direct Line 비밀을 검색합니다.Enable the Direct Line channel, and retrieve a Direct Line secret. 이러한 함수를 봇 코드 및 C# 함수에 추가합니다.You will add these to your bot code and C# function.

    자세한 내용은 봇을 관리하는 방법 및 봇을 Direct Line 연결하는방법을 참조하세요.For more information, see how to manage a bot and how to connect a bot to Direct Line.

C# 함수 만들기Create the C# function

  1. .NET Core 런타임 스택을 기반으로 Azure Functions 앱을 만듭니다.Create an Azure Functions app based on the .Net Core runtime stack.

    자세한 내용은 함수 앱을 만드는 방법 및 Azure Functions C# 스크립트 참조를 참조하세요.For more information, see how to create a function app and the Azure Functions C# script reference.

  2. 함수 DirectLineSecret 앱에 애플리케이션 설정을 추가합니다.Add a DirectLineSecret application setting to the Function App.

    자세한 내용은 함수 앱을 관리하는 방법을 참조하세요.For more information, see how to manage your function app.

  3. 함수 앱 내에서 Azure Queue Storage 템플릿을기반으로 함수를 추가합니다.Within the Function App, add a function based on the Azure Queue Storage template.

    원하는 큐 이름을 설정하고 이전 Azure Storage Account 단계에서 만든 를 선택합니다.Set the desired queue name, and choose the Azure Storage Account created in an earlier step. 이 큐 이름은 파일의 봇appsettings.js 배치됩니다.This queue name will also be placed in the bot's appsettings.json file.

  4. function.proj 파일을 함수에 추가합니다.Add a function.proj file to the function.

    <Project Sdk="Microsoft.NET.Sdk">
        <PropertyGroup>
            <TargetFramework>netstandard2.0</TargetFramework>
        </PropertyGroup>
    
        <ItemGroup>
            <PackageReference Include="Microsoft.Bot.Connector.DirectLine" Version="3.0.2" />
            <PackageReference Include="Microsoft.Rest.ClientRuntime" Version="2.3.4" />
        </ItemGroup>
    </Project>
    
  5. run.csx를 다음 코드로 업데이트합니다.Update run.csx with the following code:

    #r "Newtonsoft.Json"
    
    using System;
    using System.Net.Http;
    using System.Text;
    using Newtonsoft.Json;
    using Microsoft.Bot.Connector.DirectLine;
    using System.Threading;
    
    public static async Task Run(string queueItem, ILogger log)
    {
        log.LogInformation($"C# Queue trigger function processing");
    
        JsonSerializerSettings jsonSettings = new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore };
        var originalActivity =  JsonConvert.DeserializeObject<Activity>(queueItem, jsonSettings);
        // Perform long operation here....
        System.Threading.Thread.Sleep(TimeSpan.FromSeconds(15));
    
        if(originalActivity.Value.ToString().Equals("option 1", CompareOptions.OrdinalIgnoreCase))
        {
            originalActivity.Value = " (Result for long operation one!)";
        }
        else if(originalActivity.Value.ToString().Equals("option 2", CompareOptions.OrdinalIgnoreCase))
        {
            originalActivity.Value = " (A different result for operation two!)";
        }
    
        originalActivity.Value = "LongOperationComplete:" + originalActivity.Value;
        var responseActivity =  new Activity("event");
        responseActivity.Value = originalActivity;
        responseActivity.Name = "LongOperationResponse";
        responseActivity.From = new ChannelAccount("GenerateReport", "AzureFunction");
    
        var directLineSecret = Environment.GetEnvironmentVariable("DirectLineSecret");
        using(DirectLineClient client = new DirectLineClient(directLineSecret))
        {
            var conversation = await client.Conversations.StartConversationAsync();
            await client.Conversations.PostActivityAsync(conversation.ConversationId, responseActivity);
        }
    
        log.LogInformation($"Done...");
    }
    

봇 만들기Create the bot

  1. C# 다중 턴 프롬프트 샘플의 복사본으로 시작합니다.Start with a copy of the C# Multi-Turn-Prompt sample.

  2. Azure.Storage.Queues NuGet 패키지를 프로젝트에 추가합니다.Add the Azure.Storage.Queues NuGet package to your project.

  3. 이전에 만든 Azure Storage 계정에 대한 연결 문자열과 스토리지 큐 이름을 봇의 구성 파일에 추가합니다.Add the connection string for the Azure Storage account you created earlier, and the Storage Queue Name, to your bot's configuration file.

    큐 이름이 이전에 큐 트리거 함수를 만드는 데 사용한 것과 동일한지 확인합니다.Ensure the queue name is the same as the one you used to create the Queue Trigger Function earlier. 또한 MicrosoftAppId 이전에 봇 채널 등록 리소스를 만들 때 생성한 및 속성에 대한 값을 MicrosoftAppPassword 추가합니다.Also add the values for the MicrosoftAppId and MicrosoftAppPassword properties that you generated earlier when you created the Bot Channels Registration resource.

    appsettings.jsonappsettings.json

    {
      "MicrosoftAppId": "<your-bot-app-id>",
      "MicrosoftAppPassword": "<your-bot-app-password>",
      "StorageQueueName": "<your-azure-storage-queue-name>",
      "QueueStorageConnection": "<your-storage-connection-string>"
    }
    
  4. IConfiguration 검색하기 위해 DialogBot.cs에 매개 변수를 MicrsofotAppId 추가합니다.Add an IConfiguration parameter to DialogBot.cs in order to retrieve the MicrsofotAppId. 또한 OnEventActivityAsync Azure Function에서 에 대한 처리기를 LongOperationResponse 추가합니다.Also add an OnEventActivityAsync handler for the LongOperationResponse from the Azure Function.

    Bots\DialogBot.csBots\DialogBot.cs

    protected readonly IStatePropertyAccessor<DialogState> DialogState;
    protected readonly Dialog Dialog;
    protected readonly BotState ConversationState;
    protected readonly ILogger Logger;
    private readonly string _botId;
    
    /// <summary>
    /// Create an instance of <see cref="DialogBot{T}"/>.
    /// </summary>
    /// <param name="configuration"><see cref="IConfiguration"/> used to retrieve MicrosoftAppId
    /// which is used in ContinueConversationAsync.</param>
    /// <param name="conversationState"><see cref="ConversationState"/> used to store the DialogStack.</param>
    /// <param name="dialog">The RootDialog for this bot.</param>
    /// <param name="logger"><see cref="ILogger"/> to use.</param>
    public DialogBot(IConfiguration configuration, ConversationState conversationState, T dialog, ILogger<DialogBot<T>> logger)
    {
        _botId = configuration["MicrosoftAppId"] ?? Guid.NewGuid().ToString();
        ConversationState = conversationState;
        Dialog = dialog;
        Logger = logger;
        DialogState = ConversationState.CreateProperty<DialogState>(nameof(DialogState));
    }
    
    public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default)
    {
        await base.OnTurnAsync(turnContext, cancellationToken);
    
        // Save any state changes that might have occurred during the turn.
        await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
    }
    
    protected override async Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
    {
        // The event from the Azure Function will have a name of 'LongOperationResponse'
        if (turnContext.Activity.ChannelId == Channels.Directline && turnContext.Activity.Name == "LongOperationResponse")
        {
            // The response will have the original conversation reference activity in the .Value
            // This original activity was sent to the Azure Function via Azure.Storage.Queues in AzureQueuesService.cs.
            var continueConversationActivity = (turnContext.Activity.Value as JObject)?.ToObject<Activity>();
            await turnContext.Adapter.ContinueConversationAsync(_botId, continueConversationActivity.GetConversationReference(), async (context, cancellation) =>
            {
                Logger.LogInformation("Running dialog with Activity from LongOperationResponse.");
    
                // ContinueConversationAsync resets the .Value of the event being continued to Null, 
                //so change it back before running the dialog stack. (The .Value contains the response 
                //from the Azure Function)
                context.Activity.Value = continueConversationActivity.Value;
                await Dialog.RunAsync(context, DialogState, cancellationToken);
    
                // Save any state changes that might have occurred during the inner turn.
                await ConversationState.SaveChangesAsync(context, false, cancellationToken);
            }, cancellationToken);
        }
        else
        {
            await base.OnEventActivityAsync(turnContext, cancellationToken);
        }
    }
    
  5. 처리해야 하는 작업을 큐에 대기하는 Azure Queues 서비스를 만듭니다.Create an Azure Queues service to queue activities which need to be processed.

    AzureQueuesService.csAzureQueuesService.cs

    /// <summary>
    /// Service used to queue messages to an Azure.Storage.Queues.
    /// </summary>
    public class AzureQueuesService
    {
        private static JsonSerializerSettings jsonSettings = new JsonSerializerSettings()
            {
                Formatting = Formatting.Indented,
                NullValueHandling = NullValueHandling.Ignore
            };
    
        private bool _createQueuIfNotExists = true;
        private readonly QueueClient _queueClient;
    
        /// <summary>
        /// Creates a new instance of <see cref="AzureQueuesService"/>.
        /// </summary>
        /// <param name="config"><see cref="IConfiguration"/> used to retrieve
        /// StorageQueueName and QueueStorageConnection from appsettings.json.</param>
        public AzureQueuesService(IConfiguration config)
        {
            var queueName = config["StorageQueueName"];
            var connectionString = config["QueueStorageConnection"];
    
            _queueClient = new QueueClient(connectionString, queueName);
        }
    
        /// <summary>
        /// Queue and Activity, with option in the Activity.Value to Azure.Storage.Queues
        ///
        /// <seealso cref="https://github.com/microsoft/botbuilder-dotnet/blob/master/libraries/Microsoft.Bot.Builder.Azure/Queues/ContinueConversationLater.cs"/>
        /// </summary>
        /// <param name="referenceActivity">Activity to queue after a call to GetContinuationActivity.</param>
        /// <param name="option">The option the user chose, which will be passed within the .Value of the activity queued.</param>
        /// <param name="cancellationToken">Cancellation token for the async operation.</param>
        /// <returns>Queued <see cref="Azure.Storage.Queues.Models.SendReceipt.MessageId"/>.</returns>
        public async Task<string> QueueActivityToProcess(Activity referenceActivity, string option, CancellationToken cancellationToken)
        {
            if (_createQueuIfNotExists)
            {
                _createQueuIfNotExists = false;
                await _queueClient.CreateIfNotExistsAsync().ConfigureAwait(false);
            }
    
            // create ContinuationActivity from the conversation reference.
            var activity = referenceActivity.GetConversationReference().GetContinuationActivity();
            // Pass the user's choice in the .Value
            activity.Value = option;
    
            var message = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(activity, jsonSettings)));
    
            // Aend ResumeConversation event, it will get posted back to us with a specific value, giving us 
            // the ability to process it and do the right thing.
            var reciept = await _queueClient.SendMessageAsync(message, cancellationToken).ConfigureAwait(false);
            return reciept.Value.MessageId;
        }
    }
    

대화 상자Dialogs

이전 대화 상자를 제거하고 새 대화 상자로 바꾸어 작업을 지원합니다.Remove the old dialog and replace it with new dialogs to support the operations.

  1. UserProfileDialog.cs 파일을 제거합니다.Remove the UserProfileDialog.cs file.

  2. 사용자에게 수행할 작업을 요청하는 사용자 지정 프롬프트 대화 상자를 추가합니다.Add a custom prompt dialog that asks the user which operation to perform.

    Dialogs\LongOperationPrompt.csDialogs\LongOperationPrompt.cs

    /// <summary>
    /// <see cref="ActivityPrompt"/> implementation which will queue an activity,
    /// along with the <see cref="LongOperationPromptOptions.LongOperationOption"/>,
    /// and wait for an <see cref="ActivityTypes.Event"/> with name of "ContinueConversation"
    /// and Value containing the text: "LongOperationComplete".
    ///
    /// The result of this prompt will be the received Event Activity, which is sent by
    /// the Azure Function after it finishes the long operation.
    /// </summary>
    public class LongOperationPrompt : ActivityPrompt
    {
        private readonly AzureQueuesService _queueService;
    
        /// <summary>
        /// Create a new instance of <see cref="LongOperationPrompt"/>.
        /// </summary>
        /// <param name="dialogId">Id of this <see cref="LongOperationPrompt"/>.</param>
        /// <param name="validator">Validator to use for this prompt.</param>
        /// <param name="queueService"><see cref="AzureQueuesService"/> to use for Enqueuing the activity to process.</param>
        public LongOperationPrompt(string dialogId, PromptValidator<Activity> validator, AzureQueuesService queueService) 
            : base(dialogId, validator)
        {
            _queueService = queueService;
        }
    
        public async override Task<DialogTurnResult> BeginDialogAsync(DialogContext dc, object options, CancellationToken cancellationToken = default)
        {
            // When the dialog begins, queue the option chosen within the Activity queued.
            await _queueService.QueueActivityToProcess(dc.Context.Activity, (options as LongOperationPromptOptions).LongOperationOption, cancellationToken);
    
            return await base.BeginDialogAsync(dc, options, cancellationToken);
        }
    
        protected override Task<PromptRecognizerResult<Activity>> OnRecognizeAsync(ITurnContext turnContext, IDictionary<string, object> state, PromptOptions options, CancellationToken cancellationToken = default)
        {
            var result = new PromptRecognizerResult<Activity>() { Succeeded = false };
    
            if(turnContext.Activity.Type == ActivityTypes.Event
                && turnContext.Activity.Name == "ContinueConversation"
                && turnContext.Activity.Value != null
                // Custom validation within LongOperationPrompt.  
                // 'LongOperationComplete' is added to the Activity.Value in the Queue consumer (See: Azure Function)
                && turnContext.Activity.Value.ToString().Contains("LongOperationComplete", System.StringComparison.InvariantCultureIgnoreCase))
            {
                result.Succeeded = true;
                result.Value = turnContext.Activity;
            }
    
            return Task.FromResult(result);
        }
    }
    
  3. 사용자 지정 프롬프트에 대한 프롬프트 옵션 클래스를 추가합니다.Add a prompt options class for the custom prompt.

    Dialogs\LongOperationPromptOptions.csDialogs\LongOperationPromptOptions.cs

    /// <summary>
    /// Options sent to <see cref="LongOperationPrompt"/> demonstrating how a value
    /// can be passed along with the queued activity.
    /// </summary>
    public class LongOperationPromptOptions : PromptOptions
    {
        /// <summary>
        /// This is a property sent through the Queue, and is used
        /// in the queue consumer (the Azure Function) to differentiate 
        /// between long operations chosen by the user.
        /// </summary>
        public string LongOperationOption { get; set; }
    }
    
  4. 사용자 지정 프롬프트를 사용하여 사용자의 선택을 얻고 장기 실행 작업을 시작하는 대화 상자를 추가합니다.Add the dialog that uses the custom prompt to get the user's choice and initiates the long-running operation.

    Dialogs\LongOperationDialog.csDialogs\LongOperationDialog.cs

    /// <summary>
    /// This dialog demonstrates how to use the <see cref="LongOperationPrompt"/>.
    ///
    /// The user is provided an option to perform any of three long operations.
    /// Their choice is then sent to the <see cref="LongOperationPrompt"/>.
    /// When the prompt completes, the result is received as an Activity in the
    /// final Waterfall step.
    /// </summary>
    public class LongOperationDialog : ComponentDialog
    {
        public LongOperationDialog(AzureQueuesService queueService)
            : base(nameof(LongOperationDialog))
        {
            // This array defines how the Waterfall will execute.
            var waterfallSteps = new WaterfallStep[]
            {
                OperationTimeStepAsync,
                LongOperationStepAsync,
                OperationCompleteStepAsync,
            };
    
            // Add named dialogs to the DialogSet. These names are saved in the dialog state.
            AddDialog(new WaterfallDialog(nameof(WaterfallDialog), waterfallSteps));
            AddDialog(new LongOperationPrompt(nameof(LongOperationPrompt), (vContext, token) =>
            {
                return Task.FromResult(vContext.Recognized.Succeeded);
            }, queueService));
            AddDialog(new ChoicePrompt(nameof(ChoicePrompt)));
    
            // The initial child Dialog to run.
            InitialDialogId = nameof(WaterfallDialog);
        }
    
        private static async Task<DialogTurnResult> OperationTimeStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            // WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
            // Running a prompt here means the next WaterfallStep will be run when the user's response is received.
            return await stepContext.PromptAsync(nameof(ChoicePrompt),
                new PromptOptions
                {
                    Prompt = MessageFactory.Text("Please select a long operation test option."),
                    Choices = ChoiceFactory.ToChoices(new List<string> { "option 1", "option 2", "option 3" }),
                }, cancellationToken);
        }
    
        private static async Task<DialogTurnResult> LongOperationStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            var value = ((FoundChoice)stepContext.Result).Value;
            stepContext.Values["longOperationOption"] = value;
    
            var prompt = MessageFactory.Text("...one moment please....");
            // The reprompt will be shown if the user messages the bot while the long operation is being performed.
            var retryPrompt = MessageFactory.Text($"Still performing the long operation: {value} ... (is the Azure Function executing from the queue?)");
            return await stepContext.PromptAsync(nameof(LongOperationPrompt),
                                                        new LongOperationPromptOptions
                                                        {
                                                            Prompt = prompt,
                                                            RetryPrompt = retryPrompt,
                                                            LongOperationOption = value,
                                                        }, cancellationToken);
        }
    
        private static async Task<DialogTurnResult> OperationCompleteStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
        {
            stepContext.Values["longOperationResult"] = stepContext.Result;
            await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Thanks for waiting. { (stepContext.Result as Activity).Value}"), cancellationToken);
    
            // Start over by replacing the dialog with itself.
            return await stepContext.ReplaceDialogAsync(nameof(WaterfallDialog), null, cancellationToken);
        }
    }
    

서비스 등록 및 대화 상자Register services and Dialog

Startup.cs에서 ConfigureServices 메서드를 업데이트하여 를 LongOperationDialog 등록하고 를 추가합니다. AzureQueuesServiceIn Startup.cs, update the ConfigureServices method to register the LongOperationDialog and add the AzureQueuesService.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers().AddNewtonsoftJson();

    // Create the Bot Framework Adapter with error handling enabled.
    services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>();

    // In production, this should be a persistent storage provider.bot
    services.AddSingleton<IStorage>(new MemoryStorage());

    // Create the Conversation state. (Used by the Dialog system itself.)
    services.AddSingleton<ConversationState>();

    // The Dialog that will be run by the bot.
    services.AddSingleton<LongOperationDialog>();

    // Service used to queue into Azure.Storage.Queues
    services.AddSingleton<AzureQueuesService>();

    // Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
    services.AddTransient<IBot, DialogBot<LongOperationDialog>>();
}

봇 테스트To test the bot

  1. 아직 설치하지 않은 경우 Bot Framework Emulator를 설치합니다.If you have not done so already, install the Bot Framework Emulator.

  2. 샘플을 머신에서 로컬로 실행합니다.Run the sample locally on your machine.

  3. 에뮬레이터를 시작하고 봇에 연결한 다음, 아래와 같은 메시지를 보냅니다.Start the Emulator, connect to your bot, and send messages as shown below.

    봇 예제

추가 리소스Additional resources

도구 또는 기능Tool or feature 리소스Resources
Azure FunctionsAzure Functions 함수 앱 만들기Create a function app
C# 스크립트 Azure FunctionsAzure Functions C# script
함수 앱 관리Manage your function app
Azure PortalAzure portal 봇 관리Manage a bot
직접 회선에 봇 연결Connect a bot to Direct Line
Azure StorageAzure Storage Azure Queue StorageAzure Queue Storage
스토리지 계정을 만드는Create a storage account
Azure Portal에서 자격 증명 복사Copy your credentials from the Azure portal
큐를 사용하는 방법How to Use Queues
봇 기본 사항Bot basics 봇 작동 방식How bots work
폭포 대화 상자에 메시지 표시Prompts in waterfall dialogs
자동 관리 메시지Proactive messaging
ngrokngrok Ngrok를 사용 하 여 봇 디버그Debug a bot using ngrok