c# - ITurnContext turnContext 在使用 MS Proactive 示例中的 NotifyController 成功进行主动消息传递 1 小时后抛出 nullreference 错误
问题描述
我正在使用 microsoft-bot-framework 和使用 NotifyController 的主动示例。https://docs.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/send-proactive-messages。
问题:turnContext 抛出 nullref error,但仅在约 1 小时后。如果用户再次与机器人聊天,则没有问题,并且 turnContext 是已知的。
在 ((BotAdapter)_adapter) 处引发 nullref 错误。ContinueConversationAsync...
namespace ProactiveBot.Controllers
{
[Route("api/notify")]
[ApiController]
public class NotifyController : ControllerBase
{
private readonly IBotFrameworkHttpAdapter _adapter;
private readonly string _appId;
public NotifyController(IBotFrameworkHttpAdapter adapter, IConfiguration configuration)
{
_adapter = adapter;
_appId = configuration["MicrosoftAppId"] ?? string.Empty;
}
[HttpPost]
public async Task<IActionResult> Post([FromBody] ChatbotResponse response)
{
// Set basic reply
var reply = MessageFactory.Text(response.Text);
if(response?.Buttons?.Count > 0) // a reply with buttons
{
reply = SendSuggestedActions(response.Text,default,response?.Buttons);
}
// Target a specific user
if (response.Recipient_id != string.Empty)
{
var recipient = UserCollection.UserSession.FirstOrDefault(x => x.Value.ID == response.Recipient_id).Value;
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId,recipient.ConversationReference,(ITurnContext turnContext,CancellationToken cancellationToken) => turnContext.SendActivityAsync(reply),default);
错误日志
2021-03-31 06:22:07.559 +00:00 [Error] Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer: Connection ID "(xxxxx)", Request ID "(xxxxxx)": An unhandled exception was thrown by the application.System.NullReferenceException: Object reference not set to an instance of an object.at ProactiveBot.Controllers.NotifyController.Post(ChatbotResponse response) in C:\xxxxx\xxxxxxxx\workspace\microsoft-bot-framework\CoreBot\Controllers\NotifyController.cs:line 51at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()
------------- 更新:替代方法 -------------
await ((BotAdapter)_adapter).ContinueConversationAsync(_appId,recipient.ConversationReference,async (context,token) =>
await BotCallback(activity,context,token),default(CancellationToken));
BotCallback 是
private async Task BotCallback(Activity activity, ITurnContext turnContext, CancellationToken token)
{
var response = await turnContext.SendActivityAsync(activity);
}
抛出相同的错误。困惑为什么 turnContext 在第一个小时有效,但一个多小时后为空。
解决方案
好的,正如承诺的那样。以下是我如何主动向正在工作的用户发送消息...
你需要什么:
- Teams 中的用户 ID(
userId
在以下示例中)。您可以通过之前的交互或仅通过查询用户列表 (GetConversationPagedMembersAsync
) 获得此信息。 - 团队的租户 ID(
tenantId
在下面的示例中)。当用户最初安装我们的机器人时,我会收集这些信息并将其存储在我们的数据库中。请注意,它显然会随着时间而改变,所以我会定期再次检查它。
编码:
// we only need the user's ID, which we'll get from the DB
var user = new ChannelAccount(userId);
var conversationParams = new ConversationParameters
{
IsGroup = false,
Members = new List<ChannelAccount>
{
user
},
TenantId = tenantId
};
try
{
var conversation = await _client.Conversations.CreateConversationAsync(conversationParams);
message.Conversation = new ConversationAccount(id: conversation.Id);
var response = await _client.Conversations.SendToConversationAsync(message);
return new MessageReference
{
ConversationId = conversation.Id,
MessageId = response.Id
};
}
catch (Exception ex)
{
// log, or whatever
}
请注意,首先创建对话似乎很重要,然后才调用SendToConversationAsync
,这与消息通道不同,您似乎可以将消息活动作为ConversationParameters
.
这就是我进行所有主动消息传递的方式。我根本不使用ITurnContext
,除非我响应用户发起的消息,例如当他们第一次将机器人安装到他们的频道/个人范围时。
推荐阅读
- xml - 错误:“(”需要启动 ATTLIST 枚举
- android - 检测导航组件中的弹出
- ios - 退格不能在 swift 的正则表达式之外工作
- amazon-web-services - 如何从另一个 AWS 账户访问公共 S3 存储桶?
- c# - 如何组合 Swashbuckle 过滤器?
- swift - 如何解决 UIImageView 连接到 Swift4?
- amazon-web-services - Route53 Healthcheck 使用 Terraform 与 Cloudwatch 警报集成
- sql-server - 根据条件在 SQL Server 中运行总计
- c++ - UIAutomation:AddAutomationEventHandler() 返回 E_INVALIDARG
- angular - 如何使用 For 循环 Angular6 动态创建引导模式