C# 向带有EF的表中添加新行时违反主键约束

C# 向带有EF的表中添加新行时违反主键约束,c#,.net,sql,entity-framework,entity-framework-4,C#,.net,Sql,Entity Framework,Entity Framework 4,在我们的web应用程序中,主键约束受到了断断续续的违反(就像每隔几周就有人抱怨这一点一样)。我搜索了代码库,在这个表中创建任何行的唯一代码如下: decimal nextDocId = (from d in context.TPM_PROJECTVERSIONDOCS orderby d.DOCUMENTID descending select d.DOCUMENTID).Max() + 1; foreach

在我们的web应用程序中,主键约束受到了断断续续的违反(就像每隔几周就有人抱怨这一点一样)。我搜索了代码库,在这个表中创建任何行的唯一代码如下:

decimal nextDocId = (from d in context.TPM_PROJECTVERSIONDOCS
                     orderby d.DOCUMENTID descending
                     select d.DOCUMENTID).Max() + 1;

foreach (TPM_PROJECTVERSIONDOCS doc in documents)
{
   TPM_PROJECTVERSIONDOCS newDoc = new TPM_PROJECTVERSIONDOCS();
   newDoc.DOCUMENTID = nextDocId;
   newDoc.DOCBLOB = doc.DOCBLOB;
   newDoc.DOCUMENTNAME = doc.DOCUMENTNAME;
   newDoc.FILECONTENTTYPE = doc.FILECONTENTTYPE;
   version.TPM_PROJECTVERSIONDOCS.Add(newDoc);
   nextDocId++;
}
我们得到的错误是:

ORA-00001: unique constraint (TPMDBO.TPM_PROJECTVERSIONDOCS_PK) violated
这意味着
DOCUMENTID
已在使用中。我有一些理论来解释这是什么原因。首先,如果多人同时添加文档,则在设置
nextdocd
与保存上下文之间的某个时间段,新文档可能已添加到数据库中。然而,这一时间将只有几毫秒,所以我认为这不太可能与我们的网站获得少量的流量

我的第二个理论是EF可能会进行某种缓存,而
nextDocId
返回的缓存值不再有效

由于这个错误只会偶尔发生,当然也只发生在我们的生产服务器上,所以我没有很好的方法来调试它或重新编程

我的问题:最可能的原因是什么,有没有更好的方法来重新编写此代码以防止主键冲突?我希望主键只使用一个自动递增字段,但不幸的是Oracle不支持它们。切换到UUID也是一个解决方案,但会导致大量数据库更改。谢谢

更新:

以下是
TPM\u项目版本DOCS
实体:

<EntityType Name="TPM_PROJECTVERSIONDOCS">
   <Key>
      <PropertyRef Name="DOCUMENTID" />
   </Key>
   <Property Name="DOCUMENTID" Type="decimal" Nullable="false" />
   <Property Name="PROJECTID" Type="decimal" Nullable="false" />
   <Property Name="VERSIONID" Type="decimal" Nullable="false" />
   <Property Name="DOCUMENTNAME" Type="VARCHAR2" Nullable="false" MaxLength="500" />
   <Property Name="DOCBLOB" Type="BLOB" Nullable="false" />
   <Property Name="FILECONTENTTYPE" Type="VARCHAR2" Nullable="false" MaxLength="80" />
</EntityType>


我不知道有什么方法可以使
DOCUMENTID
默认为序列,或者使用EF查询序列。

因为您使用的是Oracle,所以应该使用Oracle序列值。它不会返回副本!调用sequence.nextval而不是max()+1将解决此问题。

(仅为未来读者发布)

我相信我有一个像样的方法来解决这个问题。我不确定这是绝对最好的方式,但我会冒险说,这比以前的方式优越得多。这就是我所做的

首先,我创建了一个新序列:

CREATE SEQUENCE TPM_PROJECTVERSIONDOCS_PK_SEQ
 START WITH     1
 INCREMENT BY   1
 NOCACHE
 NOCYCLE;
当我把它移植到生产中时,我将在当前
MAX
之后开始,而不是1;但是,我的测试数据库在此表中没有行

接下来,我在
tpmtentities
(我的实体上下文)上创建了一个扩展方法:

与:


到目前为止,这似乎很有效。

是的,我也有这个想法,但经过大量研究,我找不到一种方法让Entity Framework使用序列作为字段值。@MikeChristensen:难道你不能用一个小的存储过程从序列中检索下一个值,然后从代码中调用它吗,在插入行的过程中?将此标记为最佳答案,因为使用序列肯定是最佳方法。然而,我发布了我的实现作为一个答案,也帮助了其他人。很好的跟进。感谢您发布您的实现。
public static class EntityUtil
{
   public enum Sequence
   {
      TPM_PROJECTVERSIONDOCS_PK_SEQ
   };

   public static decimal GetNextSequence(this TPMEntities context, Sequence sequence)
   {
      string sql = String.Format("select {0}.nextval from dual", sequence.ToString());
      var testId = context.ExecuteStoreQuery<decimal>(sql);

      return testId.First();
   }
}
newDoc.DOCUMENTID = nextDocId;
newDoc.DOCUMENTID = context.GetNextSequence(EntityUtil.Sequence.TPM_PROJECTVERSIONDOCS_PK_SEQ);