为什么我的线程陷入Entity Framework SaveChangesAsync?

Gab*_*zer 2 .net c# multithreading entity-framework asp.net-web-api

我们遇到了Entity Framework的一个奇怪问题.我们正在尝试向数据库添加一个新对象,并且正在从几个不同的位置执行此操作.两者都使用通过nuget包加载的以下代码:

public async Task<Guid> AddRecurrenceHistoryAsync(RecurrenceHistory recurrenceHistory, Guid recurrenceId)
{
    _logger.DebugLogInformation("+");

    try
    {
        using (RecurringPaymentsContext context = new RecurringPaymentsContext())
        {
            var recur = context.RecurringPayments.Single(r => r.Id == recurrenceId);
            if (recur.RecurrenceHistories == null)
            {
                _logger.DebugLogInformation($"No existing histories found");
                recur.RecurrenceHistories = new List<RecurrenceHistory>();
            }
            recur.RecurrenceHistories.Add(recurrenceHistory);
            _logger.DebugLogInformation($"Saving Changes");
            var result = await context.SaveChangesAsync();
            _logger.DebugLogInformation($"Changes Saved result: {result}");
        }
    }
    catch (Exception e)
    {
        _logger.DebugLogError(e.ToString());
        throw;
    }

    _logger.DebugLogInformation(string.Format("- return: {0}", recurrenceHistory.Id));
    return recurrenceHistory.Id;
}
Run Code Online (Sandbox Code Playgroud)

从我们的控制台应用程序中执行此操作非常有效,但是我们也尝试从WebApi服务端点(下面给出的简化测试代码)执行此操作,并且从那里调用它时代码卡var result = await context.SaveChangesAsync();在线上.

Web服务测试端点:

[HttpGet]
[Route("test")]
public HttpResponseMessage Test()
{
    var paymentHistory = new RecurrenceHistory()
    {
        SystemId = Guid.NewGuid(),
        TotalAmount = -1
    };
    _logger.DebugLogInformation("Adding recurrence history");
    paymentHistory.Id = _recurrenceHistoryRepository.AddRecurrenceHistoryAsync(paymentHistory, Guid.Parse("3c412e1b-7a87-e711-910c-0050569b7221")).Result;

    return Request.CreateResponse(true);
}
Run Code Online (Sandbox Code Playgroud)

此测试端点尝试使用我们知道已经在数据库中的Guid添加对象,现在仅用于测试目的,但在运行时,代码将使其进入上述var result = await context.SaveChangesAsync();行,然后卡住.没有更多的记录,调试器停在那里,永远不会返回.不会抛出异常,但新行确实会添加到数据库中.

tl; dr摘要:

控制台应用程序可以成功调用此存储库方法来添加对象并可以继续,但Web服务可以成功地将对象添加到数据库,但代码永远不会从该context.SaveChangesAsync()方法返回.

我们尝试过的事情:

更改了有问题的失败行await Task.Run(() => context.SaveChangesAsync());,尝试将工作进程添加到服务的应用程序池,以及其他一些不值得一提的事情.

Pet*_*ons 6

不要打电话.Result,这是一个阻塞电话,更重要的是,它会导致死锁.使用await而是让你的控制器方法返回一个Task.这实际上是异步/等待的101.请参阅此内容以获得深入解释.

  • 使用C#7,我们将获得对主入口点的完全异步/等待支持.请参阅[this post](https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main/),这样可以轻松避免.Result或.在控制台应用程序中等待. (3认同)