2012-01-24 13 views
3

Webアプリケーションで主キー制約違反が非常に断続的に発生しています。私は、コードベースを検索した、と次のようにしても、このテーブル内の任意の行を作成する唯一のコードは次のとおりです。EFでテーブルに新しい行を追加すると、主キーの制約違反が発生する

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++; 
} 

我々が得るエラーは次のとおりです。これはDOCUMENTIDがすでにあることを意味

ORA-00001: unique constraint (TPMDBO.TPM_PROJECTVERSIONDOCS_PK) violated 

使用中で。私はこれを引き起こす原因についていくつかの理論を持っています。まず、nextDocIdが設定された時刻からコンテキストが保存された時刻までの間に、複数の人が同時に文書を追加していた場合、新しい文書がデータベースに追加されている可能性があります。しかし、この時間は数ミリ秒に過ぎないので、私たちのサイトには少量のトラフィックしかないとは思えません。

私の2番目の理論はおそらくEFがある種のキャッシングを行い、nextDocIdはもはや有効ではないキャッシングされた値を返している可能性があります。

このエラーはたびに頻繁に発生するため、もちろんプロダクションサーバーでのみ発生するため、デバッグや再プロビジョニングの方法がありません。

私の質問:この可能性が最も高い原因は何ですか?プライマリキー違反を防ぐためにこのコードを書き直す方が良いでしょうか?プライマリ・キーに自動インクリメント・フィールドを使用したいのですが、残念ながらOracleはそれらをサポートしていません。 UUIDに切り替えることも解決策になりますが、多くのDB変更が発生します。ありがとう!

UPDATE:

<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を使用してシーケンスを照会するために、私はどのような方法を知らないんだ:

はここTPM_PROJECTVERSIONDOCSエンティティです。

+0

ポットエンティティに参加してください –

+0

@ Floradu88 - 完了。 –

答えて

5

Oracleを使用しているため、Oracleの順序値を使用する必要があります。それは重複を返すことはありません!あなたのmax()+ 1の代わりにsequence.nextvalを呼び出すとそれが解決されます。

+0

そうですね、そのアイデアもありましたが、多くの研究の結果、Entity Frameworkにフィールド値のシーケンスを使用させる方法を見つけることができませんでした。 –

+0

@MikeChristensen:シーケンスから次の値を取得するために小さなストアドプロシージャを使用できませんでした。行を挿入するプロセスでコードから呼び出すだけですか? –

+0

うん、今私はその解決策に取り組んでいる。私はそれが最善の方法だと思う。 –

2

(ただ、将来の読者のために掲示する)

私はこの問題を解決するためのまともな方法を持っていると信じています。私はそれが絶対的な最善の方法であるかどうかはわかりませんが、私は手足に出て、それが以前にあったものよりはるかに優れていると言います。ここに私がしたことがあります。 I生産にオーバーポートこのことを、私は現在のMAXではなく、1の後に始めましょうすると

CREATE SEQUENCE TPM_PROJECTVERSIONDOCS_PK_SEQ 
START WITH  1 
INCREMENT BY 1 
NOCACHE 
NOCYCLE; 

;:

まず、私は新しいシーケンスを作成しました私のテストDBにはこのテーブルに行がありません。

次に、私は(私のエンティティコンテキスト)TPMEntitiesに拡張メソッドを作成しました:

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(); 
    } 
} 

私は各シーケンスのためenumを使用することにしました(私は今のものを持っていますが、私のように他人を持っていますシーケンスを有効にするための文字列ではなく、いくつかのIntellisenseヘルプのために)

次に、私が交換:

newDoc.DOCUMENTID = nextDocId; 

をして:

newDoc.DOCUMENTID = context.GetNextSequence(EntityUtil.Sequence.TPM_PROJECTVERSIONDOCS_PK_SEQ); 

これまでのところ、これはかなりうまく動作するようです。

+0

良いフォローアップ。実装を投稿していただきありがとうございます。 – DogLimbo

関連する問題