物业' AccountNumber'是对象关键信息的一部分,无法修改

时间:2017-03-01 17:13:26

标签: c# entity-framework entity-framework-6

我在我的应用程序中使用代码优先实体框架(代码优先)。

当我尝试添加一个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-sets 没有链接。 我查看了数据库以查看生成的内容及其预期的两个表。另一个实体&#34;客户&#34;确实有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类。 (这个 AccountNumber作为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然后再次运行修复。我仍然有同样的错误。这意味着问题肯定与我试图保存的实体无关。 EF抱怨客户等级。

在最终调用CreateCustomerLookUp方法的业务逻辑中,我正在检索客户记录并对其进行解密,我需要这样做才能读取纯文本值,但我不是要求EF保存这些更改。

所以当我保存newCustomerLookup对象时,它还在尝试保存我之前检索到的客户记录的Decrypted版本。

所以真正的问题是,我如何告诉EF我不希望它保存对该对象所做的更改?我只想保存新的newCustomerLookup对象。

2 个答案:

答案 0 :(得分:1)

根据您的更新,您需要查看DbContext的生命周期。默认情况下,DbContext会跟踪其所知的任何实体中的更改,然后在您致电SaveChanges()时尝试保存这些更改。

在这种情况下看起来似乎正在发生的事情是你在某个地方定义DbContext,与Customer合作,然后与CustomerLookup合作,同时{ {1}}处于活动状态并且知道您的实体(例如,它将知道您从数据库中检索的任何实体)。然后,当您致电DbContext时,它会尝试保存已知的所有内容。

您通常只想在需要时创建和处置SaveChanges()。他们是轻量级的,您通常希望他们能够持续工作单元(无论您的应用程序中是什么)。养成在DbContexts块中使用它们以保持清洁的习惯。所以你的方法可能类似于:

using

注意:

  1. 您还需要在代码中的其他位置进行类似的更改 本地化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(); } } 。单独进行上述更改将导致 2 DbContexts处于活跃状态,因此您可能仍会遇到其他问题 跟踪问题。

  2. 您可能会进一步限制范围 一点点重构的方法。我把整个方法都包括在内了 我看到你正在更新2个实体(DbContextscustomerLookup)而且我不知道你在做什么 newCustomerLookup

答案 1 :(得分:0)

正如“Tone&#39;发生此错误是因为EF正在尝试更新已更改的所有已知实体,即使您未明确调用:

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

在我的情况下,存储在数据库中的数据是加密的。在我的业务逻辑中,我需要取消加密客户,执行一些业务逻辑,然后更新customerLookUp记录。通过取消加密客户记录,我已经有效地更改了它,这是我尝试保存customerLookup记录时导致错误的原因。

<强>解决方案:

有几种不同的方法可以解决这个问题。一种方法是创建第二个数据上下文对象。您将使用第一个获取记录并取消加密,然后第二个将用于将实体保存到数据库。虽然这有效,但我发现它有点混乱。

我没有取消加密来自dbcontext的记录,而是将数据原样复制到新对象中。然后我取消加密那个新对象,这样我永远不会更改记录,所以当我调用saveChanges()时,EF所做的唯一更改是我明确更改的记录。

这个概念被称为&#34;数据传输对象模式&#34;你可以在这里阅读更多相关信息:

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/