Чтобы найти ответ на этот вопрос, вам действительно нужно погрузиться в исходный код, поэтому я надеюсь, что у вас есть снаряжение для подводного плавания, потому что мы углубляемся.
Шаг 1
Внутри BotController.cs
файла следующий фрагмент кода называется:
await Adapter.ProcessAsync(Request, Response, Bot);
, который вызывает метод ProcessAsync
на интерфейсе IBotFrameworkHttpAdapter
.
Шаг 2
Внутри Startup.cs
файла у нас есть следующая строка:
services.AddSingleton<IBotFrameworkHttpAdapter, BotFrameworkHttpAdapter>();
, в котором говорится, что каждый раз, когда мы запрашиваем IBotFrameworkHttpAdapter
, предоставляем один и тот же экземпляр BotFrameworkHttpAdapter
- по сути, статическую переменную, вы можете узнать больше о времени жизни службы внедрения зависимостей здесь .
Шаг 3
Внутри пакета Microsoft.Bot.Builder
у нас есть реализация для метода ProcessAsync
, которая для наших целей может быть сокращена до следующей строки:
var invokeResponse = await ProcessActivityAsync(authHeader, activity, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false);
, который вызывает ProcessActivityAsync
, которая является другой функцией, которая живет в этой библиотеке - важная часть здесь - передаваемый параметр bot.OnTurnAsync
.
Шаг 5
Также внутри пакета Microsoft.Bot.Builder
находится реализация для ProcessActivityAsync
:
return await ProcessActivityAsync(claimsIdentity, activity, callback, cancellationToken).ConfigureAwait(false);
, который вызывает перегрузку того же метода, но прежде чем мы перейдем отсюда, параметр callback
- это параметр bot.OnTurnAsync
, который был передан ранее.
Шаг 6
Перегрузка ProcessActivityAsync
также реализована внутри пакета Microsoft.Bot.Builder
и может быть упрощена до этой строки:
await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);
, где callback
равно bot.OnTurnAsync
.
Шаг 7
Еще глубже копаясь, мы находим реализацию метода RunPipelineAsync
внутри пакета Microsoft.Bot.Builder
, когда вещи начинают становиться немного размытыми ... Теоретически мы хотим перейти к else
блок, где вызывается callback
(т.е. bot.OnTurnAsync
):
// Call any registered Middleware Components looking for ReceiveActivityAsync()
if (turnContext.Activity != null)
{
// Other code
}
else
{
// call back to caller on proactive case
if (callback != null)
{
await callback(turnContext, cancellationToken).ConfigureAwait(false);
}
}
Однако на шаге 6 у нас также была эта строка:
using (var context = new TurnContext(this, activity))
, где создается контекст, а свойство действия - initialized . Этот же context
передается на вызов RunPipelineAsync
, что означает, что мы не провалимся на блок else ...
Но есть следующий комментарий к методу RunPipelineAsync
:
/// <param name="callback">A callback method to run at the end of the pipeline.</param>
и внутри секции remarks
:
...Once control reaches the end of the pipeline, the adapter calls
the <paramref name="callback"/> method...
Поэтому я уверен, что можно с уверенностью сказать, что наш метод callback
выполняется, что означает, что мы продолжаем, поднимаясь вверх по цепочке, чтобы разрешить функцию, которая callback
отображается на (bot.OnTurnAsync
).
Шаг 8
В BotController
мы передаем экземпляр IBot
методу ProcessAsync
, а в Startup
мы соединяем все запросы на IBot
, чтобы вернуть экземпляр EchoBot
следующим образом:
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot.
services.AddTransient<IBot, EchoBot>();
Реализация EchoBot
наследуется от ActivityHandler
класса:
public class EchoBot : ActivityHandler
Шаг 9
Класс ActivityHandler
обеспечивает реализацию по умолчанию для метода OnTurnAsync
, который я упрощу до:
switch (turnContext.Activity.Type)
{
case ActivityTypes.Message:
return OnMessageActivityAsync(new DelegatingTurnContext<IMessageActivity>(turnContext), cancellationToken);
// Other cases
}
который метод OnMessageActivityAsync
в том же классе, который имеет реализацию , которая возвращает выполненную задачу, т.е. это неоперативный метод, однако это виртуальный метод - классы, которые наследуют ActivityHandler
, могут обеспечить собственную реализацию.
Шаг 10
Пользовательская реализация для OnMessageActivityAsync
предоставляется внутри класса EchoBot
:
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
await turnContext.SendActivityAsync(MessageFactory.Text($"Echo: {turnContext.Activity.Text}"), cancellationToken);
}
, когда вводимые пользователем данные возвращаются к ним, и, таким образом, наше путешествие заканчивается.
Что касается шага 7, команда Microsoft довольно активно работает с SO для вещей, помеченных botframework
, поэтому может быть лучше получить @mdrichardson или @tdurnford, чтобы уточнить, что здесь происходит.
Помимо Visual Studio, вы можете отладить некоторый библиотечный код, включив следующую опцию:
- Сервис -> Параметры -> Отладчик
- Снимите флажок «Включить только мой код»
Также, если вы включите навигацию к декомпилированным источникам (вам нужно будет принять всплывающее уведомление), выполнив this :
- Сервис -> Параметры -> Текстовый редактор -> C # -> Дополнительно
- Установите флажок «Включить навигацию к декомпилированным источникам»
Вы сможете проверить исходный код внешних пакетов в самой Visual Studio.