明显的种族条件/并发问题

时间:2019-01-03 13:36:05

标签: asp.net-mvc entity-framework

我有一个使用ASP.NET MVC(C#),实体框架和Oracle数据库编写的Web应用程序。在其中一个控制器中,我具有与以下类似的代码(向下拖动以仅显示我认为对该问题必要的内容):

    using (var context = CreateContext())
    {
        //Other code ...
        var workItem = //Get work item from context

        var nextReviewers =
            await context.WorkItemReviewers
                         .Where(r => r.WorkItemId == workItem.Id)
                         .Where(r => r.Position > workItem.NextReviewerPosition)
                         .OrderBy(r => r.Position)
                         .ToArrayAsync();
        if (nextReviewers.Count() > 0)
        {
            workItem.Status = "A";
            workItem.StatusDetails = "A";
            workItem.NextReviewerPosition = nextReviewers.First().Position;

            //Other Code
        }
        else
        {
            workItem.Status = "B";
            workItem.StatusDetails = "B";
            workItem.NextReviewerPosition = null;
        }

        //Other Code

        await context.SaveChangesAsync();
    }

基于上述代码,我从不期望状态或StatusDetails的值会有所不同,但是在生产环境中,两个请求相距约3-4毫秒,现在, 莫名其妙地,我在数据库中有以下值:Status =“ B”; StatusDetails =“ A”。

我想念什么吗?是否存在基于EntityFramework(针对Oracle 11g)在ASP.NET中的行为的逻辑解释?

1 个答案:

答案 0 :(得分:1)

基于上面的代码,鉴于更新的workItem实体是在数据库上下文的范围内加载的,并且两个值是字符串,我必须以99.5%的把握确定此代码对您所看到的行为。 (尽管我肯定会看这个项目,以查看是否被证明是错误的:)我将密切关注在与对服务的调用有关的情况下使用Status或StatusDetails的所有地方。我怀疑其他一些代码意外地更改了另一个,并调用了SaveChanges。

我可以建议进行一个小的优化

var nextReviewer = context.WorkItemReviewers
    .Where(r => r.WorkItemId == workItem.Id
        && r.Position > workItem.NextReviewerPosition)
    .OrderBy(r => r.Position)
    .Select(r => new { r.Position }) // Add any other details you may need from reviewer and other related entities.
    .FirstOrDefault();

if (nextReviewer != null)
{
    workItem.Status = "A";
    workItem.StatusDetails = "A";
    workItem.NextReviewerPosition = nextReviewer.Position;
    //Other Code
}
else
{
    workItem.Status = "B";
    workItem.StatusDetails = "B";
    workItem.NextReviewerPosition = null;
}

通过使用.Select(),您可以优化查询以仅返回所需表中的列,从而使对数据库的调用更快。除非期望查询在时间上相对繁重(例如,> 500ms),否则我还要避免异步操作。目的是使服务器在处理更大的操作时响应更快。在所有内容上使用它会使所有操作的速度都比必要的慢。对返回的数据进行优化之后,如果仍然需要一些时间来解决问题,那么可以考虑使用异步。