EF中的自定义约束失败,异步问题

时间:2012-03-18 11:38:54

标签: database entity-framework asynchronous asp.net-web-api

我有一个像这样的控制器动作(ASP.NET web api)

public HttpResponseMessage<Component> Post(Component c)
{
    //Don't allow equal setup ids in within the same installation when the component is of type 5
    if (db.Components.Any(d => d.InstallationId == c.InstallationId && d.SetupId == c.SetupId && d.ComponentTypeId == 5)) return new HttpResponseMessage<Component>(c, HttpStatusCode.Conflict);

    db.Components.Add(c);
    db.SaveChanges();
    return new HttpResponseMessage<Component>(c, HttpStatusCode.OK);
}      

我从javascript发送了一些帖子请求,其中2个是相等的

{SetupId : 7, InstallationId : 1, ComponentTypeId: 5}

我已经使用fiddler和逐步执行服务器上的代码验证了这一点。

然而,有时我在上面做的约束是按照应有的方式工作,有时却不是。我猜因为Post是一个异步操作,所以请求#2有时会在第一个请求设法保存到数据库之前检查数据库是否有重复。

我该如何解决这个问题?有没有办法从后期操作开始到结束时将EF操作锁定到数据库?那是不是一个好主意?

但是,我有数据库约束,因为这只是当componenttype为5时,我不知道如何实现它。或者,如果它甚至可能。

2 个答案:

答案 0 :(得分:0)

使用EF很难实现。在普通的SQL中,您将启动事务并向约束查询添加一些表提示以强制锁定记录。问题是EF不支持表提示。您无法强制linq或ESQL查询来锁定记录。

您的选择是:

  • 手动锁定您的方法。使用例如lock将大大降低方法的吞吐量,因此您很可能需要为每个ID提供一些自定义的智能实现锁定
  • 使用自定义SQL或存储过程而不是LINQ查询和强制锁定。我认为带有UPDLOCK提示的HOLDLOCK应该适用于这种情况。
  • 或者,您可以在InstallationIdSetupIdComponentTypeId上放置唯一索引,并在并发请求尝试插入重复记录时捕获异常。问题是,如果该组合必须仅在某些情况下是唯一的,而对其他情况则不是。

答案 1 :(得分:0)

我在这个答案的帮助下在数据库中解决了这个问题:https://stackoverflow.com/a/5149263/94394 SQL Server 2008中允许的条件约束。

create unique nonclustered index [funcix_Components_setupid_installationid_RecordStatus]
on [components]([setupid], [Installationid])
where [componenttypeid] = 5

然后我捕获了DbUpdateException并检查是否有约束异常错误代码

        try
        {
            db.Components.Add(c);
            db.SaveChanges();                
        }
        catch (DbUpdateException ex) {
            Exception innermostException = ex;
            while (innermostException.InnerException != null)//Get innermost exception
            {
                innermostException = innermostException.InnerException;
            }

            if (((System.Data.SqlClient.SqlException)innermostException).Number == 2601)//Constraint exception id
            {
                return new HttpResponseMessage<Component>(c, HttpStatusCode.Conflict);    
            }
        }
        return new HttpResponseMessage<Component>(c, HttpStatusCode.OK);