2013-01-31 22 views
9

我一直在拉我的頭髮大約2天現在,因爲我根本無法獲得EF保存更改,只要我添加多對多許多實體到現有實體。更新實體框架6中的多對多導航屬性,更改不被保存

我的結構是普通的簡單:

  • 我有一個表稱爲Person,它有一個ID(主,同一性),以及一些其他的字符串字段

  • 的表稱爲Keyword與一個ID(主,同一性)和稱爲Value

  • PersonKeywordRelation,具有PersonId字符串字段,和一個KeywordId

當我生成我的實體(數據庫第一),我得到一個類Person,與ICollection<Keyword> - 都好,按預期工作。

當我嘗試保存現有Person時,出現了問題,並帶有修改後的關鍵字列表。只有標量屬性(字符串)被保存,而不是我的關鍵字!

  • 我試過禁用延遲加載,沒有效果。
  • 我嘗試從數據庫中再次加載每個單獨的關鍵字,沒有效果。
  • 我嘗試將所有關鍵字加載到上下文中,以查看是否有助於EF檢測到更改,但沒有。

我很確定我不是唯一一個有過這個問題的人(事實上我完全相信,因爲我已經在這裏看到過幾個關於同一主題的問題,但我無法找到一個可行的答案...),主要針對較老版本的EF,這是另一個很好的理由,爲什麼我又開始了另一個問題:沒有任何改變能解決這個問題呢?

這是我的代碼,更新(和創建)的人。你會注意到我試圖讓EF保存相應的更改。

public void SavePersons(IList<Person> persons) 
    { 
     // Create a EF Context 
     using (var ctx = new MyDbEntities()) 
     { 
      foreach (var person in persons) 
      { 
       // Attach 
       ctx.Persons.Attach(person); 

       // Insert or update? 
       ctx.Entry(person).State = person.Id == 0 ? EntityState.Added : EntityState.Modified; 

       // Get current keywords before clearing from entity 
       var keywords = new List<Keyword>(person.Keywords); 

       // Clear keywords from entity, so we can add fresh ones, hopefully 
       // EF will have an easier time handling this.. 
       person.Keywords.Clear(); 

       // Add keywords 
       keywords.ForEach(kw => 
       { 
        ctx.Keywords.Attach(kw); 
        ctx.Entry(kw).State = EntityState.Modified; 
        person.Keywords.Add(kw); 
       });    
      } 

      // Save 
      ctx.SaveChanges(); 
     } 
    } 
+0

您的關鍵字的值是否改變或者是人與關鍵字之間的關係是否改變? –

+0

只有關係 - 但兩者都應該得到支持,最好。 – Jeff

回答

11

最後。最後我可以休息了!我找到了解決方案!這不是一個漂亮的,但它的工作原理!

下面是代碼共享關懷。

這絕對是我最後一次與Entity Framework合作。導致更多的痛苦&痛苦比好。

public void SavePersons(IList<Person> persons) 
    { 
     // Create a EF Context 
     using (var ctx = new MyDbEntities()) 
     { 
      // Iterate 
      foreach (var person in persons) 
      { 
       // Get current keywords 
       var keywords = new List<Keyword>(person.Keywords).ToList(); 

       // Fetch Person from DB (if its not a NEW entry). Must use Include, else it's not working. 
       var newPerson = ctx.Persons 
             .Include("Keywords") 
             .FirstOrDefault(s => s.Id == person.Id) ?? person; 

       // Clear keywords of the object, else EF will INSERT them.. Silly. 
       newPerson.Keywords.Clear(); 

       // Insert or update? 
       ctx.Entry(newPerson).State = newPerson.Id == 0 ? EntityState.Added : EntityState.Modified; 

       // Apply new scalar values 
       if(newPerson.Id != 0) 
       { 
        person.Id = newPerson.Id; 
        ctx.Entry(newPerson).CurrentValues.SetValues(person); 

       } 

       // Iterate through all keywords 
       foreach (var kw in ctx.Keywords) 
       { 
        // If the current kw exists in OUR list, add it 
        // - if not, remove the relation from the DB. 
        if (keywords.Any(k => k.Id == kw.Id)) 
        { 
         //ctx.Entry(kw).State = EntityState.Unchanged; 
         ctx.Keywords.Attach(kw); 
         newPerson.Keywords.Add(kw); 
        } 
        else 
         newPerson.Keywords.Remove(kw); 
       } 
      } 

      // Save 
      ctx.SaveChanges(); 

     } 
    } 
+1

這幫了我,所以謝謝你發佈你的解決方案。 關鍵是在獲取實體時使用'Includes'。我想我的問題與跟蹤導航屬性的上下文有關。我不想禁用延遲加載,因爲在將實體映射到其DTO時會導致問題。 – chrisjsherm

+0

我花了很長時間才弄明白,我多次發佈了這個問題的答案,然後我不得不刪除它,因爲進一步的測試揭示了錯誤。 – Jeff

+0

以下是關於如何編寫通用版本的代碼的相關問題http://goo.gl/2sCky –

2

嘗試增加.ToList()

var keywords = new List<Keyword>(person.Keywords).ToList();//generate list sepereate from .Keywords 

我懷疑保溼之前,你的關鍵詞列表永遠不會填充,因爲你清楚它。

+0

不能在代碼塊中使用** bold **標記,刪除星號並替換爲註釋 – AaronLS

+0

這沒有做到:/ – Jeff

1

所以下面是未經測試,但修復我的錯誤;)它應該有希望做的伎倆。我不知道代碼的其餘部分,因此我選擇創建輸入數據的克隆,並按特定順序將對象附加到上下文。

編輯:改名方法

// get unique list of Keywords from all Persons 
    private List<Keyword> getUniqueKeywords(IEnumerable<Person> oxygenThiefs) { 
     var result = new List<Keyword>(); 

     foreach (var thief in oxygenThiefs) { 
      foreach (var keyword in thief.Keywords) { 
       if (!result.Contains(keyword)) { 
        result.Add(keyword); 
       } 
      } 
     } 

     return result; 
    } 

    // shallow clone of Person 
    private Person clonePerson(Person target) { 

     return new Person { 
      Id = target.Id, 
      Name = target.Name, 
      .. 
      .. 

     }; 
    } 

    public void SavePersons(IList<Person> persons) { 
     // Create a EF Context 
     using (var ctx = new MyDbEntities()) { 

      // add all Keywords to the Context so that they are tracked 
      foreach (var keyword in getUniqueKeywords(persons)) { 
       ctx.Keywords.Attach(keyword); 
       // if value of Keyword has actually changed then uncomment line 
       // ctx.Entry(keyword).State = EntityState.Modified 
      } 

      foreach (var person in persons) { 

       // hehe 
       var shallowPerson = clonePerson(person); 

       // Attach Person 
       ctx.Persons.Attach(shallowPerson); 

       // Establish relationship (however shallow and meaningless) 
       foreach (var keyword in person.Keywords) { 
        shallowPerson.Keywords.Add(keyword); 
       } 

       // Insert or update? 
       ctx.Entry(shallowPerson).State = person.Id == 0 ? EntityState.Added : EntityState.Modified; 

      } 

      // Save 
      ctx.SaveChanges(); 
     } 
    } 
+0

Urgh,第3條評論,可惜它沒有工作......我太快了。 – Jeff

+0

贊成'IEnumerable oxygenThiefs'。你讓我今天一整天都感覺很好! :D –

相關問題