2013-03-25 90 views
0

我有兩個我想存儲在EF Code First中的類。一棟大樓有一名維護人員和一份在那裏工作的人員名單。 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; } 
} 

這給了我與基本屬性兩個表,和Building_Id上的人,和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,因爲一個單一的實體。不能被識別爲異常的來源,通過在你的實體類型中公開外鍵屬性,可以更容易地處理異常,同時保存更加容易。

with innerexception: 「無法確定依賴操作的有效排序。由於外鍵約束,模型要求或存儲生成的值,可能存在依存關係。」

我不想在我的Poco's上公開任何更多的屬性,因爲他們是Poco的,我對這些屬性沒有用處。如果我必須滿足Code First模型生成的要求,那麼就不得不這樣做,但如果可能的話,我希望在映射課程中遠離我的Poco's。

我該如何解決這個問題?

回答

2

因此,出現此問題的原因是您的實體之間存在循環關係,導致EF在嘗試解決對SaveChanges的單個調用中的所有插入操作時放棄EF,並引發您所看到的異常。

要理解爲什麼它不能處理這種情況,我們可以考慮嘗試保存實體時數據庫中會發生什麼情況。

使用你的代碼,你可以把它沒有錯誤的SaveChanges之前註釋掉最後一行運行被調用,但後來人m1不會建立b2合作,所以這不是你想要的。

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

然而,EF能夠通過在數據庫中創建以下插入運行此:

  1. 插入人m1。因爲m1無效,所以將FK留給建築物表格爲空。
  2. 插入建築物'b1'。因爲m1保持b1,所以使用id1作爲FK。
  3. 插入人m2。使用b1的ID作爲FK,因爲b1是其中m2的作品。
  4. 插入建築物b2。使用ID爲m2作爲FK,因爲m2保持b2

現在,它很容易看出爲什麼它時,你包括使得b2m1工作行不行。
在上面的第一個插入中,EF無法將FK作爲空值,因爲您告訴它需要指向建築物,但該建築物尚未插入,因此無法創建FK指向它。
當實體具有循環依賴關係時,這在EF中始終是個問題。如果兩者都相互依賴,則不能在單次提交中創建插入。

解決您的問題只需撥打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. 

未來支持
好像這個問題將在EF的未來版本中得到解決。

從ORM期望它應該能夠處理插入父項,插入子項,然後使用新創建的子ID更新父項是合理的。

您可以閱讀更多關於它並投票支持Microsoft Connect的功能。

還有一些信息在EF CodePlex site.

0

嘗試映射關係。在您的上下文是從派生的DbContext,覆蓋OnModelCreating方法:

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

我也嘗試使用[關鍵]表示在實體的主鍵,但我不能完全確定,如果你要去做。

像這樣:

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

我現在不能嘗試了這一點,但我希望它能幫助。