EchoBot:如何控制从BotController到EchoBot的流程 [英] EchoBot: How does control flow from BotController to EchoBot

查看:44
本文介绍了EchoBot:如何控制从BotController到EchoBot的流程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在查看EchoBot示例并试图理解它.我看到BotController映射到api/messages和HttpPost,后者依次调用Adapter.ProcessAsync.但是,这如何转换为EchoBot.OnMessageActivityAsync调用?我试图设置一个断点并查看调用堆栈,但这无济于事(请参阅所附的屏幕截图).

我了解BotFrameworkHttpAdapter是通过依赖项注入来调用的.但是我不知道我们最终将如何进入EchoBot.

解决方案

要真正找到答案,您必须深入源代码,所以我希望您拥有水肺潜水的装备,因为我们将继续深入

第1步

BotController.cs 内部此处

第3步

Microsoft.Bot.Builder 包中,我们有 ProcessActivityAsync 的Builder/BotFrameworkAdapter.cs#L241"rel =" nofollow noreferrer>实现:

 返回等待ProcessActivityAsync(claimsIdentity,activity,回调,cancellationToken).ConfigureAwait(false); 

调用相同方法的重载,但是在我们从这里继续之前, callback 参数是之前通过的 bot.OnTurnAsync 参数./p>

第6步

ProcessActivityAsync 的重载也是行:

  using(var context = new TurnContext(this,activity)) 

创建上下文的位置,活动属性为

以及备注部分中的内容:

  ...一旦控制到达管道的末尾,适配器将调用< paramref name ="callback"/>方法... 

所以我可以肯定地说,我们的 callback 方法正在执行,这意味着我们通过冒泡备份链来继续解析 callback 映射的功能,从而继续到( bot.OnTurnAsync ).

第8步

BotController 中,我们将 IBot 的实例传递给 ProcessAsync 方法,而在 Startup 中,我们进行布线将所有对 IBot 的请求返回给 EchoBot 的实例,如下所示:

 //将Bot创建为瞬态.在这种情况下,ASP控制器需要一个IBot.services.AddTransient< IBot,EchoBot>(); 

EchoBot

将用户的输入回显给他们,因此我们的旅程结束了.


关于第7步,Microsoft团队对于使用 botframework 标记的事情非常活跃,因此最好获取@mdrichardson或@tdurnford来澄清这里发生的事情.


除了在Visual Studio中,您还可以通过启用以下选项来调试一些库代码:

此外,如果您通过执行

您将能够检查Visual Studio本身中外部软件包的源代码.

I am looking at the EchoBot sample and trying to understand it. I see that BotController is mapped to api/messages and HttpPost which in turn invokes Adapter.ProcessAsync. But how does this translate into EchoBot.OnMessageActivityAsync call? I tried to set up a breakpoint and see the call stack but that doesn't help ( see attached screenshot).

I understand BotFrameworkHttpAdapter is invoked via dependency injection. But I don't know how we end up in EchoBot eventually.

解决方案

To find the answer to this you really have to dive into the source code, so I hope you've got your scuba diving gear because we're going deep.

Step 1

Inside the BotController.cs file the following piece of code is called:

await Adapter.ProcessAsync(Request, Response, Bot);

which calls the ProcessAsync method on the IBotFrameworkHttpAdapter interface.

Step 2

Inside the Startup.cs file we have the following line:

services.AddSingleton<IBotFrameworkHttpAdapter, BotFrameworkHttpAdapter>();

which says that every time we ask for an IBotFrameworkHttpAdapter, provide the same instance of BotFrameworkHttpAdapter - essentially a static variable, you can read more about dependency injection service lifetimes here.

Step 3

Inside the Microsoft.Bot.Builder package we have the implementation for the ProcessAsync method, that can for our purposes be reduced to the following line:

var invokeResponse = await ProcessActivityAsync(authHeader, activity, bot.OnTurnAsync, cancellationToken).ConfigureAwait(false);

which calls ProcessActivityAsync which is another function that lives in this library - the important part here is the bot.OnTurnAsync parameter being passed in.

Step 5

Also inside the Microsoft.Bot.Builder package is the implementation for ProcessActivityAsync:

return await ProcessActivityAsync(claimsIdentity, activity, callback, cancellationToken).ConfigureAwait(false);

which calls an overload of the same method, but before we move on from here, the callback parameter is the bot.OnTurnAsync parameter that was passed through before.

Step 6

The overload of ProcessActivityAsync is also implemented inside the Microsoft.Bot.Builder package, and can be simplified to this line:

await RunPipelineAsync(context, callback, cancellationToken).ConfigureAwait(false);

where callback is bot.OnTurnAsync.

Step 7

Digging deeper still we find the implementation of the RunPipelineAsync method inside of the Microsoft.Bot.Builder package which is were things start to get a bit fuzzy... Theoretically we want to fall through to the else block where the callback (i.e. bot.OnTurnAsync) is called:

// 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);
    }
}

However, back in Step 6 we also had this line:

using (var context = new TurnContext(this, activity))

where the context is created, and the activity property is initialised. This same context is pass through to the RunPipelineAsync call, which means that we will not fall through to the else block...

But there are the following comment on the RunPipelineAsync method:

/// <param name="callback">A callback method to run at the end of the pipeline.</param>

and inside the remarks section:

...Once control reaches the end of the pipeline, the adapter calls
the <paramref name="callback"/> method...

So I believe is it safe to say that our callback method is being executed which means that we continue by bubbling back up the chain to resolve the function that callback maps to (bot.OnTurnAsync).

Step 8

In BotController we pass in an instance of IBot to the ProcessAsync method, and in Startup we wire up all requests for an IBot to return an instance of EchoBot like so:

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

The EchoBot implementation inherits from the ActivityHandler class:

public class EchoBot : ActivityHandler

Step 9

The ActivityHandler class provides a default implementation for the OnTurnAsync method which I will simplify to:

switch (turnContext.Activity.Type)
{
   case ActivityTypes.Message:
       return OnMessageActivityAsync(new DelegatingTurnContext<IMessageActivity>(turnContext), cancellationToken);
   // Other cases
}

which the OnMessageActivityAsync method on the same class that has an implementation that returns a completed task, i.e. is a no-op, however it is a virtual method - classes which inherit ActivityHandler can provide their own implementation.

Step 10

A custom implementation for OnMessageActivityAsync is provided inside the EchoBot class:

protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
    await turnContext.SendActivityAsync(MessageFactory.Text($"Echo: {turnContext.Activity.Text}"), cancellationToken);
}

where the user's input is echoed back to them, and thus our journey ends.


Regarding Step 7, the Microsoft team are pretty active on SO for things tagged with botframework so it might be best to get @mdrichardson or @tdurnford to clarify what happens here.


As an aside in Visual Studio you can debug some library code by enabling the following option:

  • Tools --> Options --> Debugger
  • Uncheck "Enable Just my Code"

Also if you enable navigation to decompiled sources (you will have to accept a legal notification popup) by doing this:

  • Tools --> Options --> Text Editor --> C# --> Advanced
  • Check "Enable navigation to decompiled sources"

You will be able to inspect the source code of external packages within Visual Studio itself.

这篇关于EchoBot:如何控制从BotController到EchoBot的流程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆