2014-08-28 62 views
0

我正在實現一個導入例程,其中用戶將特定的格式化字符串粘貼到輸入字段中,然後將輸入字段轉換爲實體,然後放入數據庫。不能「添加或更新」實體,因爲Primery密鑰不能更改

算法檢查實體是否已經存在,並嘗試更新它或將其插入到數據庫中。插入工作正常 - 更新失敗。

//considered existing if Name and owning user match. 
if (db.Captains.Any(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId)) 
{ 
    var captainToUpdate = db.Captains.Where(cpt => cpt.Name == captain.Name && cpt.User.Id == UserId).SingleOrDefault(); 

    db.Entry(captainToUpdate).CurrentValues.SetValues(captain); 
    db.Entry(captainToUpdate).State = EntityState.Modified; 
    await db.SaveChangesAsync(); 
} 

眼前的問題是,這樣寫的,它會嘗試更新主鍵爲好,(captain ID爲0,而captainToUpdate標識已被設置),這導致異常The property 'Id' is part of the object's key information and cannot be modified.

我需要更改什麼,以便正確更新實體。如果可以避免,我不想手動更新每個屬性,因爲表隊長包含30個列。

+0

爲什麼不在設置值之前記下鍵值。然後將鑰匙重置。 – 2014-08-28 13:04:11

回答

2

我不會用實體Captain將數據傳輸到用戶界面,但有要複製的所有屬性,並沒有更多的DTO的對象。 您可以從任何對象複製值。所有匹配的屬性將被複制,captainToUpdate中的所有其他屬性將不受影響。

+0

如果有任何用處。我將數據從用戶界面傳輸到服務器。它是一個字符串,它被解析並壓入一個實體類型隊長,因爲這是最適合的。身份證和導航屬性很害羞。 – Marco 2014-08-29 04:40:34

+1

並不重要,你可以隨時使用DTO類型。無論如何,你必須告訴哪些屬性應該複製。 AutoMapper(配置忽略的屬性)也可以完成這項工作。 – 2014-08-29 06:52:05

1

嘗試這樣?

var captainToUpdate = db.Captains.FirstOrDefault(cpt => cpt.Name == captain.Name &&  cpt.User.Id == UserId); 
if(captainToUpdate != null){//Update captain Here 
    captainToUpdate.Update(captain);   
}else{//Create captain here 
    db.Captains.Add(captain); 
} 
db.Savechanges(); 
+0

這當然是一個有效的解決方案,但正如我的最後一句話所述,我正在尋找一種避免這種情況的方法。我不想手動更新30多個物業。 – Marco 2014-08-28 13:44:45

+0

創建方法更新需要隊長參數... – 2014-08-28 13:47:38

+1

您只需添加代碼即可升級這些屬性。我最近不得不爲10個具有10-80個屬性的實體編寫類似的代碼,其中包含轉換選項集和布爾值等特殊行爲。有時你必須做一些你不想做的事情。 – Nzall 2014-08-28 13:48:44

2

你可以做的是首先將隊長的ID設置爲與隊長更新的ID相同。在這種情況下使用:

db.Entry(captainToUpdate).CurrentValues.SetValues(captain); 

將工作。

+0

這給了我找到問題的線索:) – Romias 2017-11-16 00:26:52

0

我遇到了同樣的問題,並通過擴展方法和反射來解決它,當然最好是創建一個獨立的類,並重新安裝一些緩存,但性能對我的任務並不重要。

public static class EnitityFrameworkHelper 
    { 
     public static void SetValuesByReflection(this DbPropertyValues propertyValues, object o, IEnumerable<string> properties = null) 
     { 
      var reflProperties = o.GetType().GetProperties(); 
      var prop = properties ?? propertyValues.PropertyNames; 
      foreach (var p in prop) 
      { 
       var refp = reflProperties.First(x => x.Name == p); 
       var v= refp.GetValue(o); 
       propertyValues[p] = v; 
      } 
     } 
    } 

,這裏是例如如何使用它

var entry = ctx.Entry(accSet); 
entry.CurrentValues.SetValuesByReflection(eParameters, entry.CurrentValues.PropertyNames.Except(new [] { "ID"})); 

而且要小心要更新對象外鍵,大概要排除他們。

相關問題