这些异步调用中的哪一个与另一个不同?

时间:2014-07-21 02:43:00

标签: c# entity-framework async-await

我怀疑我遇到了死锁问题,但这是一个奇怪的问题,我无法理解。我有一个API需要验证一些事情来处理呼叫。作为业务逻辑的一部分,我可能还需要进行更多相同的调用。在这种情况下,如果未找到与实体关联的特定数据,我们将尝试使用备份(如果已配置),这需要检查其他实体。最终,代码将挂起。

让我们深入研究代码(评论突出显示有问题的电话)。

API控制器:

public async Task<HttpResponseMessage> Get(int entityID, string content, bool? useBackUp = true)
{
    //Some look-ups here, no issues at all

    //This works, but it's this method that has an issue later in the process.
    SystemEntity entityObj =
            await BusinessLayer.GetSystemEntityAsync(SystemEntityID);

    if (entityObj == null)
    {
        return new HttpResponseMessage
        {
            StatusCode = System.Net.HttpStatusCode.BadRequest,
            Content = new StringContent("Entity is unavailable.")
        };
    }

    string text = BusinessLayer.GetContentTextAsync(entityID
            new List<string> {contentName}, useBackUp).Result.FirstOrDefault().Value;

    if (text == null)
    {
        return new HttpResponseMessage {StatusCode = System.Net.HttpStatusCode.NoContent};
    }

    return new HttpResponseMessage
    {
        StatusCode = System.Net.HttpStatusCode.OK,
        Content = new StringContent(text)
    };
}

业务层:

public async Task<Dictionary<string, string>> GetContentTextAsync(int systemEntityID, List<string> contentNames, bool useBackUp)
{
    Dictionary<string, string> records = new Dictionary<string, string>();

    //We iterate for caching purposes
    foreach (string name in contentNames)
    {
        string nameCopy = name;
        string record = Cache.GetData(
            string.Format("{0}_{1}_{2}", CONTENT, systemEntityID, name), () =>
                DataLayer.GetCotnent(systemEntityID, nameCopy));

        if (record == null && useBackUp)
        {
            List<int> entityIDs = new List<int> {systemEntityID};
            int currentEntityID = systemEntityID;

            //Here's that method again. This call seems to work.
            SystemEntity currentEntity = await GetSystemEntityAsync(systemEntityID);

            if (currentEntity != null && currentEntity.BackUpID.HasValue)
            {
                currentEntityID = (int) currentEntity.BackUpID;
            }

            while (!entityIDs.Contains(currentEntityID))
            {
                int id = currentEntityID;

                record = Cache.GetData(
                    string.Format("{0}_{1}_{2}", CONTENT, systemEntityID, name), () =>
                        DataLayer.GetCotnent(id, nameCopy));

                if (record != null) break;

                entityIDs.Add(currentEntityID);

                //This call seems to cause the deadlock
                currentEntity = await GetSystemEntityAsync(currentEntityID);

                if (currentEntity != null && currentEntity.BackUpID.HasValue)
                {
                    currentEntityID = (int) currentEntity.UseBackupID;
                }
            }
        }

        if (record != null)
        {
            records.Add(name, record);
        }
    }

    return records;
}

public async Task<SystemEntity> GetSystemEntityAsync(int systemEntityID)
{
    SystemEntity systemEntity = await DataLayer.GetSystemEntity(
        scc => scc.SystemEntityID == systemEntityID);

    return systemEntity;
}

数据层:

public async Task<SystemEntity> GetSystemEntity(Expression<Func<SystemEntity, bool>> whereExpression)
{
    using (EntityContext dbContext = createDbInstance())
    {
        //This is the last line that the debugger in VS 2013 brings me to. Stepping into this returns to whatever called the API method, waiting endlessly.
        return await
            dbContext.SystemEntity.Include(sc => sc.OtherEntity).Where(whereExpression).FirstOrDefaultAsync();
    }
}

回顾一下:我调用了GetSystemEntityAsync三次。前两次,它成功完成。第三次,它挂起了。如果我注释掉前两个呼叫,那么它们根本就不运行,第三个呼叫仍然挂起。如果我删除了await并在数据层方法的return语句中只使用了一个普通的FirstOrDefault,那么一切都很好。

注意:我必须使GetSystemEntityAsync方法保持异步。我无法改变它是同步的。

我遇到的僵局有哪些可能的来源?我不知道如何解决它。

1 个答案:

答案 0 :(得分:6)

  

这些异步调用中的哪一个与另一个不同?

这个,我怀疑:

  string text = BusinessLayer.GetContentTextAsync(entityID
            new List<string> {contentName}, useBackUp).Result.FirstOrDefault().Value;

尝试将其更改为:

  string text = (await BusinessLayer.GetContentTextAsync(entityID
            new List<string> {contentName}, useBackUp)).FirstOrDefault().Value;

Stephen Cleary在他的"Don't Block on Async Code"博文中描述了僵局的可能来源。