实体框架查询失败“任务已取消。”

CJ *_*ten 2 c# entity-framework-core .net-core

我有一个 ASP Core 3.1 Web 项目,我想向其中添加 EntityFramework Core。
我创建了一个数据库上下文,即具有数据库操作的模型类,并将其注入到我的主类(Azure Bot)中。

但是,当我尝试将记录插入数据库时​​,它总是失败并出现错误

System.Threading.Tasks.TaskCanceledException:“任务已取消。”

这是我的startup.cs:

services.AddDbContext<IVRContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection"),
                optionBuilder => optionBuilder.EnableRetryOnFailure()
            )
        );
        services.AddTransient<IVRCallModel>();
Run Code Online (Sandbox Code Playgroud)

这是我正在调用的 IVRModel 中的函数:

 public async Task InsertCallAsync(IVRCall call)
    {
        try
        {
            await _ivrContext.Calls.AddAsync(call);
            await _ivrContext.SaveChangesAsync();
        }
        catch (Exception ex)
        {
            throw new Exception(ex.Message, ex);
        }
    }
Run Code Online (Sandbox Code Playgroud)

我就是这样称呼它的:

private async Task NotificationProcessor_OnNotificationReceivedAsync(NotificationEventArgs args)
{
    this.GraphLogger.CorrelationId = args.ScenarioId;
    if (args.ResourceData is Call call)
    {
        if (call.Direction != CallDirection.Outgoing && call.ToneInfo == null)
        {
            if (args.ChangeType == ChangeType.Created && call.State == CallState.Incoming)
            {
                await SaveCall(call.Id, call.CallChainId, "Incoming");
                
                .... code removed 
            }
        }
    }
}

private async Task SaveCall(string callId, string callChainId, string callState, string redirectCallId = null)
{
    IVRCall newCall = new IVRCall();
    newCall.Id = callId;
    newCall.CallChainId = callChainId;
    newCall.TimeStamp = DateTime.Now.ToString("dd-MM-yyyy HH:mm:ss");
    newCall.State = callState;
    newCall.RedirectCallId = redirectCallId; 
    await _ivrCallModel.InsertCallAsync(newCall);
}
Run Code Online (Sandbox Code Playgroud)

编辑:“原始”NoticicationProcessor_OnNotificationReceived 函数,它调用异步方法。(来自微软示例项目)

private void NotificationProcessor_OnNotificationReceived(NotificationEventArgs args)
{
    _ = NotificationProcessor_OnNotificationReceivedAsync(args).ForgetAndLogExceptionAsync(this.GraphLogger, $"Error processing notification {args.Notification.ResourceUrl} with scenario {args.ScenarioId}");
}
Run Code Online (Sandbox Code Playgroud)

Gab*_*uci 8

当代码在响应已发送到客户端后尝试运行时,就会发生该异常。当您使用 async/await 时,当某处未等待某些异步内容时,可能会发生这种情况。

事实上,这就是发生在这里:

private void NotificationProcessor_OnNotificationReceived(NotificationEventArgs args)
{
    _ = NotificationProcessor_OnNotificationReceivedAsync(args).ForgetAndLogExceptionAsync(this.GraphLogger, $"Error processing notification {args.Notification.ResourceUrl} with scenario {args.ScenarioId}");
}
Run Code Online (Sandbox Code Playgroud)

await当作用于一个不完整的对象时Task,它实际上会返回。它返回Task自己的 a ,以便调用者可以跟踪它何时完成。因此,当NotificationProcessor_OnNotificationReceivedAsync返回时,工作实际上尚未完成。由于您没有使用Task,因此执行会继续,并且 ASP.NET 会打包请求并结束该请求的任务。

将事件处理程序与异步代码一起使用可能会很棘手,特别是在这种情况下,您需要暂停执行直到工作完成,否则您会得到意外的结果。您可以使用类似的方法同步等待异步代码.GetAwaiter().GetResult(),但这可能会产生意想不到的后果。

我认为最好的选择是在这种情况下只使用同步代码:使用.SaveChanges()而不是.SaveChangesAsync().

关于您对 的使用.AddAsync()文档说:

此方法是异步的,仅允许特殊值生成器(例如“Microsoft.EntityFrameworkCore.Metadata.SqlServerValueGenerationStrategy.SequenceHiLo”使用的生成器)异步访问数据库。对于所有其他情况,应使用非异步方法。

.Add()所以无论如何你应该一直使用,即使在异步方法中也是如此。

try关于您的/块的评论catch也是有效的。如果您要捕获一个异常只是为了使用相同的消息抛出一个新异常,那么根本不要捕获该异常。即使您需要在再次抛出异常之前进行一些日志记录或其他操作,也请使用 justthrow;而不是throw new Exception(ex.Message, ex);. 引发新的异常会更改异常发生位置的堆栈跟踪,并使调试变得困难。