2017-03-01 73 views
0

我在我的應用程序中使用代碼第一個實體框架(代碼優先)。屬性'AccountNumber'是對象的關鍵信息的一部分,不能修改

當我嘗試添加一個NEW對象並將其保存到數據庫時,EF會拋出此錯誤。

屬性'AccountNumber'是對象的關鍵信息 的一部分,無法修改。

首先令人困惑的是這個錯誤是AccountNumber不是該表中的主鍵。

讀幾篇文章來解決此之後,我嘗試將其保存這樣之前創建一個全新的對象:

public void CreateCustomerLookUp(CustomerLookUp customerLookUp) 
    { 
     //To avoid EF specific error recreating object using NEW - wierd! 
     //http://stackoverflow.com/questions/3187963/the-property-id-is-part-of-the-objects-key-information-and-cannot-be-modified 

     customerLookUp.LastUpdated = DateTime.Now; 

     var encryptedCustomerLookUp = EncryptCustomerLookUp(customerLookUp); 

     var newCustomerLookup = new CustomerLookUp() 
     { 
      AccountNumber = encryptedCustomerLookUp.AccountNumber, 
      EmailAddress = encryptedCustomerLookUp.EmailAddress, 
      MobileNumber = encryptedCustomerLookUp.MobileNumber, 
      UmbracoMemberId = encryptedCustomerLookUp.UmbracoMemberId, 
      UpdateCode = encryptedCustomerLookUp.UpdateCode, 
      UpdateNotification = encryptedCustomerLookUp.UpdateNotification 
     }; 

     customerDataContext.Entry(newCustomerLookup).State = EntityState.Added;//<< Throwing error here! 
     customerDataContext.SaveChanges(); 
    } 

我也曾嘗試

customerDataContext.CustomerLookUps.Add(newCustomerLookup); 

而不是

customerDataContext.Entry(newCustomerLookup).State = EntityState.Added; 

但我仍然收到錯誤。這是我的CustomerLookup實體的外觀,我無法弄清楚爲什麼EF認爲AccountNumber是一個Key而非它。 (我想也許主鍵和鍵是兩個不同的東西?)

public class CustomerLookUp 
{ 
    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public int Id { get; set; } 

    [StringLength(256)] 
    public string AccountNumber { get; set; } 

    [StringLength(256)] 
    public string MobileNumber { get; set; } 
    [StringLength(256)] 
    public string EmailAddress { get; set; } 
    [StringLength(256)] 
    public string UpdateCode { get; set; } 

    [StringLength(256)] 
    public string UmbracoMemberId { get; set; } 

    public bool UpdateNotification { get; set; } 

    public DateTime LastUpdated { get; internal set; } 
} 

這是我的上下文類只有2 DB-套鏈接。 我已經在數據庫中查看了預期生成的內容和兩個表。另一個實體「客戶」確實具有AccountNumber作爲PK,但我不想保存到該表中。

public partial class CustomerContext : DbContext 
{ 
    public CustomerContext() 
     : base("name=CustomerContext") 
    { 
    } 

    public DbSet<Customer> Customers { get; set; } 
    public DbSet<CustomerLookUp> CustomerLookUps { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
    } 



} 

爲了完整起見,這是Customer類。 (此具有賬戶號碼作爲PK,但這是一個完全不同的表,而不是一個我想太節省

public class Customer 
{ 
    [Key] 
    [Required] 
    [StringLength(256)] 
    public string AccountNumber { get; set; } 

    [Required] 
    [StringLength(256)] 
    public string SupplyPostCode { get; set; } 

    [StringLength(256)] 
    public string Title { get; set; } 

    [StringLength(256)] 
    public string Forenames { get; set; } 

    [StringLength(256)] 
    public string Name { get; set; } 



} 

更新:我已經取得了進展,但沒有完全解決問題我想做的是重命名列,所以我在CustomerLookUp類的AccountNumber的每個表中有兩個不同的名稱我將名稱更改爲AccountNumberX然後再次運行cure我仍然得到相同的錯誤。問題與我試圖挽救的實體無關,EF正在抱怨客戶類別。他最終調用CreateCustomerLookUp方法的業務邏輯我正在檢索客戶記錄並解密它,我需要這樣做來讀取純文本值,但我不要求EF保存這些更改。

因此,當我保存newCustomerLookup對象時,它正在嘗試保存先前檢索到的客戶記錄的Decrypted版本。

所以真正的問題是,我如何告知EF我不希望它保存對該對象所做的更改?我只想保存新的newCustomerLookup對象。

+1

您可以檢查主鍵定義在表中您的數據庫? – Sparrow

+0

請發佈您的CustomerDataContext的代碼。 –

+0

您是否將屬性設置爲上下文類中的鍵? – DavidG

回答

0

爲在「音」,因爲EF試圖更新已經改變了所有已知的實體出現此錯誤答案解釋,即使你不顯式調用:

customerDataContext.Entry(newCustomerLookup).State = EntityState.Added; 
customerDataContext.SaveChanges(); 

在我的案例存儲在數據庫中的數據被加密。在我的業務邏輯中,我需要對客戶進行解密,執行一些業務邏輯,然後更新customerLookUp記錄。通過解密客戶記錄,我有效地改變了它,這是當我嘗試保存customerLookup記錄時導致錯誤的原因。

解決方案:

有幾種不同的方法來解決這個問題。一種方法是創建第二個數據上下文對象。您將使用第一個獲取記錄並將其解密,然後第二個將用於將實體保存到數據庫。雖然這有效,但我覺得這是一個混亂的解決方案。

而不是解密來自dbcontext的記錄,我將數據按原樣複製到新對象中。然後,我將這個新對象解密,這樣我就不會更改記錄,所以當我調用saveChanges()時,EF所做的唯一更改就是我明確更改的記錄。

這個概念被稱爲「數據傳輸對象模式」,你可以閱讀更多關於它在這裏:

https://docs.microsoft.com/en-us/aspnet/web-api/overview/data/using-web-api-with-entity-framework/part-5

https://www.exceptionnotfound.net/entity-framework-and-wcf-mapping-entities-to-dtos-with-automapper/

1

根據您的更新,您需要查看您的DbContext的使用期限。A DbContext默認情況下會跟蹤其意識到的任何實體的變化,然後在撥打SaveChanges()時嘗試保存這些更改。

所以看起來在這種情況下要發生的事情是你定義一個DbContext的地方,用你的Customer工作,那麼你的工作CustomerLookup,所有而DbContext是活動的,瞭解你的實體(例如,它會知道您從數據庫中檢索到的任何實體)。然後,當您撥打SaveChanges()時,它正試圖保存它所知道的所有變化。

您通常只需要創建和處理DbContexts。它們是輕量級的,你通常希望它們持續一個工作單元(不管你的應用程序中可能存在什麼)。養成在using區塊中使用它們的習慣,保持乾淨。所以,你的方法可能看起來像:

public void CreateCustomerLookUp(CustomerLookUp customerLookUp) 
{ 
    using (var context = new CustomerContext()) 
    { 
     customerLookUp.LastUpdated = DateTime.Now; 

     var encryptedCustomerLookUp = EncryptCustomerLookUp(customerLookUp); 

     var newCustomerLookup = new CustomerLookUp() 
     { 
      AccountNumber = encryptedCustomerLookUp.AccountNumber, 
      EmailAddress = encryptedCustomerLookUp.EmailAddress, 
      MobileNumber = encryptedCustomerLookUp.MobileNumber, 
      UmbracoMemberId = encryptedCustomerLookUp.UmbracoMemberId, 
      UpdateCode = encryptedCustomerLookUp.UpdateCode, 
      UpdateNotification = encryptedCustomerLookUp.UpdateNotification 
     }; 

     context.Entry(customerLookUp).State = EntityState.Modified; 
     context.CustomerLookUps.Add(newCustomerLookup); 
     context.SaveChanges(); 
    } 
} 

注:

  1. 您還需要做出改變的其他地方在代碼中同樣 本地化DbContexts。單獨進行上述更改將導致處於活動狀態,因此您可能仍然會遇到其他跟蹤問題 。

  2. 您可能會在 方法中進一步限制範圍,並進行一些重構。我已經把整個方法作爲 我看你正在更新2個實體(customerLookupnewCustomerLookup),我不知道你在做什麼 EncryptCustomerLookup

+1

很好的解釋,(投了票) - 我已經弄清楚發生了什麼,並且曾經按照您的建議做過並且使用了不同的dbcontext對象來完成保存。 - 但是,我確實遇到了其他跟蹤問題。所以我想出了一個解決這個問題的不同方法,所以我會回答我自己的問題並分享解決方案。 –

相關問題