2011-03-31 31 views
27

我正在使用實體框架4.1(代碼優先)處理一個小樣本項目。我的課是這樣的:實體框架代碼優先 - 爲什麼我不能以這種方式更新複雜屬性?

public class Context : DbContext 
{ 
    public IDbSet<Person> People { get; set; } 
    public IDbSet<EmployeeType> EmployeeTypes { get; set; } 
} 

public class Person 
{ 
    [Key] 
    public int Key { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 

    virtual public EmployeeType EmployeeType { get; set; } 
} 

public class EmployeeType 
{ 
    [Key] 
    public int Key { get; set; } 
    public string Text { get; set; } 

    virtual public ICollection<Person> People { get; set; } 
} 

我保存了一對夫婦EmployeeTypes(「第一」,「第二」)到數據庫中,我已經保存了一個人誰具有第一種類型。現在我想修改Person。我知道我可以通過加載人員,更改屬性,然後保存來做到這一點。但我想做的事情,而不是,這似乎對我來說,它應該工作,是這樣的:

var c = new Context(); 
var e = c.EmployeeTypes.Single(x => x.Text.Equals("second")); 
var p = new Person { 
      Key = originalKey,  // same key 
      FirstName = "NewFirst", // new first name 
      LastName = "NewLast", // new last name 
      EmployeeType = e };  // new employee type 
c.Entry(p).State = EntityState.Modified; 
c.SaveChanges(); 

奇怪的是,這改變了名字和姓氏,但不能EmployeeType。如果我得到一個新的上下文並請求這個Person,那麼EmployeeType仍然被設置爲「first」,就像這個代碼運行之前一樣。

我需要做什麼才能讓導航屬性更新,而不僅僅是標量屬性?(這是特別令人費解的,因爲對於EmployeeType,唯一需要改變的是Person表中的外鍵,並且該鍵是標量屬性。)

(順便說一句,我知道我可以做通過先檢索Person,然後逐個更改屬性,但是因爲我在ASP.NET MVC中使用了模型綁定,所以似乎這種方式會更容易,因爲我將更新的人員對象已經存在於我的POST方法)

回答

19

,您可以嘗試不同的方式:

var c = new Context(); 
var e = c.EmployeeTypes.Single(x => x.Text.Equals("second")); 
var p = new Person { 
      Key = originalKey,  // same key 
      FirstName = "NewFirst", // new first name 
      LastName = "NewLast"}; // new last name 
c.People.Attach(p); // Attach person first so that changes are tracked 
c.Entry(p).Reference(e => e.EmployeeType).Load();    
p.EmployeeType = e; // Now context should know about the change 
c.Entry(p).State = EntityState.Modified; 
c.SaveChanges(); 

另一種方法是暴露的外鍵我ñ喜歡你Person實體:

public class Person 
{ 
    [Key] 
    public int Key { get; set; } 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    [ForeignKey("EmployeeType")] 
    public int EmployeeTypeKey { get; set; } 
    public virtual EmployeeType EmployeeType { get; set; } 
} 

這將PersonEmployeeType之間的獨立相關變化的關係的類型外鍵關聯。而不是分配導航屬性分配外鍵屬性。這將允許您通過當前代碼修改關係。

問題是獨立的關聯(那些不使用外鍵屬性)在狀態管理器/更改跟蹤器中作爲單獨的對象處理。所以你對人的修改並不影響現有關係的狀態,也沒有設定新的關係。 I asked on MSDN如何用DbContext API做到這一點,但只有在投射DbContextObjectContext並使用ObjectStateManagerChangeRelationshipState纔有可能。

+0

呃,這真的可以嗎?我寧願將EmployeeType標記爲Modified以及Person。雖然當我嘗試時,它也沒有工作。 – 2011-03-31 21:08:45

+0

由於獨立聯繫而無法工作 - 正如我所說它是單獨的對象。可能還有其他方法。我會修改我的答案。 – 2011-03-31 21:10:54

+0

我試過它的「其他方式」:「DbUpdateException:保存不爲其關係提供外鍵屬性的實體時發生錯誤。EntityEntries屬性將返回null,因爲單個實體不能被識別爲異常的來源。通過在您的實體類型中公開外鍵屬性,可以更輕鬆地處理保存的異常。有關詳細信息,請參閱InnerException。 – 2011-03-31 21:20:48

2

在嘗試了十幾種不同的方式來完成EF方式之後,我得出結論認爲,沒有一個合理的EF Code First方法可以做我想做的事情。所以我用反射。我創建了我的課該方法從DbContext繼承:

public void UpdateFrom<T>(T updatedItem) where T : KeyedItem 
{ 
    var originalItem = Set<T>().Find(updatedItem.Key); 
    var props = updatedItem.GetType().GetProperties(
     BindingFlags.Public | BindingFlags.Instance); 
    foreach (var prop in props) 
    { 
     var value = prop.GetValue(updatedItem, null); 
     prop.SetValue(originalItem, value, null); 
    } 
} 

我所有的物體從一個抽象類繼承,並有一個共同的主鍵屬性,因此這個發現現有的對象以相同的密鑰作爲一個傳遞在,然後更新現有的對象從新的。之後需要調用SaveChanges

+2

順便說一下,在這種類型的幾個問題之後(特別是對於「延遲」加載(現在我明白名稱改變了,因爲微軟的延遲加載與延遲加載沒有任何共同之處)),我放棄並切換到城堡ActiveRecord,我曾與之合作過,並且做了我所需要的工作,而不需要尷尬的解決方法。 – 2011-04-20 17:36:36

+0

這是我在過去幾天的想法 – 2011-06-24 16:20:51

1

這適用於收藏,雖然我覺得有一個更好的方法。


var properties = typeof(TEntity).GetProperties(); 
foreach (var property in properties) 
{ 
    if (property.GetCustomAttributes(typeof(OneToManyAttribute), false).Length > 0) 
    { 
     dynamic collection = db.Entry(e).Collection(property.Name).CurrentValue; 
     foreach (var item in collection) 
     { 
      if(item.GetType().IsSubclassOf(typeof(Entity))) 
      { 
       if (item.Id == 0) 
       { 
        db.Entry(item).State = EntityState.Added; 
       } 
       else 
       { 
        db.Entry(item).State = EntityState.Modified; 
       } 
      } 
     } 
    } 
} 
db.Entry(e).State = EntityState.Modified;    
db.SaveChanges(); 

相關問題