0

私はSOの検索を試みましたが、私が見つけたすべての結果は既にDBに保存されているエンティティのPKの更新に対応しています。私の場合は違う。複数のリンクされた1-1のリレーションがPKを期待通りに同期していません

私は1-0.1の関係を持つデータベースに3つのテーブルを持っています。関係は次のようになります。「< - 」

A <- B <- C 

主要エンドに関係してポイントを表します。私。各Bには常に関連するAがありますが、AにはBがありません。つまり、Aの基数は1、Bの値は0..1です。

各関係は、子エンティティのPKから親エンティティのPKに向かうFKによって表されます。各PKは、クライアント生成値を持つuniqueidentifierId列です。私は同じ基数を持つ同じ関係を持つデータベースからEF 4モデルを生成しました。

BCという子エンティティを既存のAエンティティに追加しようとしています。設計上の理由から、いくつかの新しいインスタンスが1つの平和なコードで作成され、Aエンティティは別のエンティティのBエンティティにリンクされています。さらに、後者はCが存在することを知りたくありません。

public B CreateB() 
{ 
    return new B 
    { 
     Id = Guid.NewGuid(), 
     C = new C(), 
    }; 
} 

そして今リンクとコードを保存します:ここで

BC作成コードがどのように見えるかだ

// a is an instance of A that has been loaded from DB 
// and hence has a persistent Id value. 
// b is a just-created instance of B 
// that has a non-persistent Id value and null reference to A. 
void SaveBlahBlahBlah(A a, B b) 
{ 
    // At this point b and c have the same Id value. 
    // It differs from a's Id, but that's expected, they haven't been linked yet. 
    b.A = a; 
    // At this point b receives a's Id value, but c keeps the original one, 
    // therefore the existing b-c link gets broken! 

    using(var ctx = new MyContext()) 
    { 
    ctx.As.Attach(a); // This throws exception saying 
    // I've violated referential integrity. 
    // It doesn't say which relationship is broken, 
    // but I guess it's the B-C one since 
    // the debugger shows them to have different values if PKs 

    ctx.Bs.AddObject(b); 

    ctx.SaveChanges(); 
    } 
} 

私は(両方ともデフォルトEFのコードジェネレータでこれを試してみました生成されたエンティティの基本クラスとしてEFのEntityクラスを使用するもの)およびセルフトラッキングエンティティコードジェネレータを使用します。結果は同じです。

コードがクラッシュします。その理由は、ABがリンクされた後に、BCが1-1の関係を持つエンティティで違法な違うPK値を取得する可能性があります。

私が期待していたのは、Cが自動的に値をBAから取得した値に同期しているということでした。オブジェクトグラフで作業しているので妥当だと思われます。既存のB - Cの関係はOKですが、BAをリンクしても問題ありません。なぜそれは壊れますか? DBにBまたはCのいずれかが存在し、私がPKを変更できなかった場合、私はそれを理解するでしょう。しかし、それは事実ではない、両方のentitesはちょうど作成されています。

EFはPKであるために1-1の関係の両側を必要とするため、FKのPK列とは別のものを使用して鍵のチェーンを破ることはできません。

私はキーを手動で同期させたくありません。実際には、関連するテーブルが1つ以上あり、同期コードが多くの場所に表示される必要があります。

STEジェネレータのT4テンプレートを更新して、PKアップデートをカスケード接続して1-1の関係にすることができると信じています。しかし、私はあまりにもT4に精通していないとあまりにもそれを行うことは幸せではない。

私は2つの質問がある:

  1. 私の場合、カスケード接続PK更新の私の期待は、いくつかの理由で間違ってますか? (しかし、奇妙に見えます)私は、それはバグか機能ですか?
  2. STEテンプレートを変更する以外に、問題を解決する方法が他にもありますが、できれば簡単ですか? EFのマッピングや文脈での魔法のオプションがあるかもしれませんか?

ありがとうございます。

答えて

3

問題は、1つの参照オブジェクトから別の参照オブジェクトへのIDの割り当てを処理するサービスがコンテキストであることです。しかし、実際にアソシエーションを作った時点では、どちらのオブジェクトもコンテキストにはありません。コンテキストにBを追加すると関係が修正されるため、これは通常問題ではありません。

残念ながら、あなたはこれをしません。代わりに、Aとの追加の関係を作成しますが、コンテキストに嘘をつき、すべてがすでに修正されていると主張します。より正確には、EntitySet.Attachと呼びますが、これは実際にはすでに修正されたオブジェクトのみを対象としています。

一方

、このようなコードはうまく動作するはずです:すべての私はここにやった

public B CreateB() 
{ 
    return new B 
    { 
     Id = Guid.NewGuid(), 
     C = new C(), 
    }; 
} 

void SaveBlahBlahBlah(A a, B b) 
{  
    using(var ctx = new MyContext()) 
    {  
    ctx.Bs.AddObject(b); 

    ctx.SaveChanges(); 
    } 
} 

ノートはBとCの間の関係とは何の関係もありません、問題のコードは、削除されます

つまり、Attachに注意してください。あなたは、あなたがそれを呼び出すときに何をしているのかを知る必要があります。

UPDATE

Aの既存のインスタンスを処理したバージョン:

void SaveBlahBlahBlah(A a, B b) 
{  
    Debug.Assert(a.B != b); 

    using(var ctx = new MyContext()) 
    {  
    ctx.As.Attach(a); 

    a.B = b; // it's crucial that this link is set after attaching a to context! 

    ctx.Bs.AddObject(b); 

    ctx.SaveChanges(); 
    } 
} 
+1

+1非常に明確な説明! –

+0

ありがとうございます。私はAttachを使用したことに注意してください。EFがなければ、DBにAがすでに存在していたのでエラーにつながったAを挿入しようとしました。私はモデルとフレーバーを変更しました(私は当時からEFCFを使用していました)。だから、あなたの提案がうまくいくかもしれない、私はそれを試してみましょう。 –

+0

あなたは 'Attach'を使うことができますが、Bとの関係を作る前にそれをする必要があります。 –

関連する問題