我有一个像这样的控制器动作(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时,我不知道如何实现它。或者,如果它甚至可能。答案 0 :(得分:0)
使用EF很难实现。在普通的SQL中,您将启动事务并向约束查询添加一些表提示以强制锁定记录。问题是EF不支持表提示。您无法强制linq或ESQL查询来锁定记录。
您的选择是:
lock
将大大降低方法的吞吐量,因此您很可能需要为每个ID提供一些自定义的智能实现锁定UPDLOCK
提示的HOLDLOCK
应该适用于这种情况。InstallationId
,SetupId
和ComponentTypeId
上放置唯一索引,并在并发请求尝试插入重复记录时捕获异常。问题是,如果该组合必须仅在某些情况下是唯一的,而对其他情况则不是。答案 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);