0

EFコードファーストに保存したいクラスが2つあります。建物にはメンテナとそこで働く人々のリストがあります。mainainerは建物で働く必要はありません。

私の最初の試みで私はちょうど持っていた

public class Person
    {
        public virtual Guid Id { get; set; }
        public virtual string Name { get; set; }

    }

public class Building
{
    public virtual  Guid Id { get; set; }
    public virtual List<Person> WorksHere { get; set; }

    public virtual Person MaintainedBy { get; set; }
    public virtual String Address { get; set; }
}

これにより、基本プロパティを持つ2つのテーブル、PeopleのBuilding_Id、およびBuildingsのMaintainedBy_Idが得られます。

テストプログラムを実行すると

        using (TestContext tc = new TestContext())
        {
            Person m1 = tc.persons.Create();
            m1.Name = "maintainB1";
            m1.Id = Guid.NewGuid();

            Person m2 = tc.persons.Create();
            m2.Name = "maintainB2";
            m2.Id = Guid.NewGuid();

            Building b1 = tc.buildings.Create();
            b1.Address = "building1";
            b1.Id = Guid.NewGuid();
            tc.buildings.Add(b1);

            Building b2 = tc.buildings.Create();
            b2.Address = "building1";
            b2.Id = Guid.NewGuid();
            tc.buildings.Add(b2);

            b1.MaintainedBy = m1;
            b2.MaintainedBy = m2;

            if (b1.WorksHere == null) b1.WorksHere = new List<Person>();
            if (b2.WorksHere == null) b2.WorksHere = new List<Person>();


            b1.WorksHere.AddRange(new List<String>() { "e11", "e12", "e13" }.Select(s =>
            {
                Person p = new Person();
                p.Id = Guid.NewGuid();
                p.Name = s;
                return p;
            }));

            b2.WorksHere.AddRange(new List<String>() { "e21", "e22", "e23" }.Select(s =>
            {
                Person p = new Person();
                p.Id = Guid.NewGuid();
                p.Name = s;
                return p;
            }));


            b1.WorksHere.Add(m2);
            b2.WorksHere.Add(m1);
            tc.SaveChanges();
        }

    }

例外が発生します:「関係の外部キープロパティを公開しないエンティティの保存中にエラーが発生しました。単一のエンティティを例外のソースとして識別できないため、EntityEntriesプロパティはnullを返します。保存中の例外の処理は次のようになります。エンティティタイプの外部キープロパティを公開することで簡単になります。詳細については、InnerExceptionを参照してください。」

内部例外あり:「依存操作の有効な順序を決定できません。外部キーの制約、モデル要件、またはストアで生成された値が原因で、依存関係が存在する可能性があります。」

私のPocoでこれ以上プロパティを公開したくないのは、それらがPocoであり、これらのプロパティを使用できないためです。コードファーストモデルの生成を満たす必要がある場合は、それよりも必要ですが、可能であれば、マッピングクラスのPocoからそれを遠ざけたいと思います。

これを修正するにはどうすればよいですか?

4

2 に答える 2

2

したがって、この問題は、エンティティ間に循環関係があるために発生します。これにより、への1回の呼び出しですべての挿入を解決しようとすると、EFが諦め、表示されてSaveChangesいる例外が発生します。

この状況を処理できない理由を理解するために、エンティティを保存しようとしたときにデータベースで何が起こるかを考えてみましょう。

SaveChangesコードを使用すると、呼び出される前の最後の行をコメントアウトすることでエラーなしで実行できますが、その場合、人m1はビルドb2に従事しないため、これはあなたが望むものではありません。

b1.WorksHere.Add(m2);
//b2.WorksHere.Add(m1); <-- When this is removed it works.. 
tc.SaveChanges();

ただし、EFは、データベースに次の挿入を作成することでこれを実行できます。

  1. 人を挿入しm1ます。m1どこでも機能しないため、FKを建物テーブルにnullのままにします。
  2. 建物「b1」を挿入します。m1を維持するため、FKとして「m1」のIDを使用しb1ます。
  3. 人を挿入しm2ます。が機能するb1ため、FKとしてidを使用します。b1m2
  4. 建物を挿入しますb2。を維持するm2ため、FKとしてidを使用します。m2b2

m1これで、で機能する行を含めると、なぜ機能しないのかを簡単に確認できますb2
上記の最初の挿入では、建物を指す必要があると言っているため、EFはFKをnullのままにすることができませんが、その建物はまだ挿入されていないため、FKポインティングを作成できません。それに戻ります。
エンティティに循環依存がある場合、これはEFでは常に問題になります。両方が相互に依存している場合、1回のコミットで挿入を作成することはできません。

問題の解決策は、に2つの呼び出しを行うことSaveChangesです。m1で作業を行う直前にそれを呼び出しb2、その後再び呼び出すと、正しい種類の動作が得られます。

b1.WorksHere.Add(m2);
tc.SaveChanges(); <-- Create inserts. FK in m1 is null because he works nowhere yet.
b2.WorksHere.Add(m1);
tc.SaveChanges(); <-- Updates FK in m1 to point to b2.

EntityFrameworkでの将来のサポート
この問題はEFの将来のバージョンで解決されるようです。

ORMからは、親の挿入、子の挿入、および新しく作成された子IDでの親の更新を処理できる必要があると期待するのが妥当です。

詳細を読んで、 MicrosoftConnectに実装される機能に投票することができます。

EFCodePlexサイトにもいくつかの情報があります。

于 2013-03-27T23:14:18.177 に答える
0

関係をマッピングしてみてください。DbContextから派生したコンテキストで、OnModelCreatingメソッドをオーバーライドします。

protected override void OnModelCreating(DbModelBuilder mb)
{
    mb.Entity<Building>()
        .HasMany(m => m.LivesHere)
        .WithRequired()
        .Map(n => n.MapKey("Home_Id"));
}

また、[Key]を使用してエンティティの主キーを示してみますが、そうする必要があるかどうかは正確にはわかりません。

そのようです:

public class Building
{
    [Key]
    public virtual  Guid Id { get; set; }
    public virtual List<Person> WorksHere { get; set; }

    public virtual Person MaintainedBy { get; set; }
    public virtual String Address { get; set; }
}

今は試せませんが、お役に立てば幸いです。

于 2013-03-27T14:25:10.257 に答える