【问题标题】:The property 'AccountNumber' is part of the object's key information and cannot be modified属性“AccountNumber”是对象的关键信息的一部分,不能修改
【发布时间】:2017-03-01 17:13:25
【问题描述】:

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

当我尝试添加新对象并将其保存到数据库时,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 个数据库集 链接。 我查看了数据库以查看生成的内容及其两个表是否符合预期。另一个实体“客户”确实有 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 在抱怨 Customer 类。

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

所以发生的情况是,当我保存 newCustomerLookup 对象时,它也在尝试保存我之前检索到的客户记录的解密版本。

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

【问题讨论】:

  • 你能在你的数据库中查看表中的主键定义吗?
  • 请发布您的 CustomerDataContext 的代码。
  • 您是否将属性设置为上下文类中的键?
  • 检查了数据库...客户查表没有得到Account as PK
  • SaveChanges 是否抛出异常?在调试模式下,检查 Customer 类型的 DbContext 上附加的实体是什么,并查看是否有正在修改的属性 AccountNumberCustomerCustomerLookup之间也有FK关系吗?

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


【解决方案1】:

根据您的更新,您需要查看DbContext 的生命周期。默认情况下,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 DbContexts 处于活跃状态,因此您很可能仍会遇到其他 跟踪问题。

  2. 您可能会进一步将范围限制在 方法与一些重构。我已将整个方法封装为 我看到您正在更新 2 个实体(customerLookupnewCustomerLookup) 我不知道你在做什么 EncryptCustomerLookup.

【讨论】:

  • 很好的解释,(投票赞成)-我已经弄清楚发生了什么,并且有一次我按照您的建议做了,并使用了不同的 dbcontext 对象来进行保存。 - 但是是的,我确实遇到了其他跟踪问题。所以我想出了一个不同的方法来解决这个问题,所以我会回答我自己的问题并分享解决方案。
【解决方案2】:

正如“Tone”的回答中所解释的,发生此错误是因为 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/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-12
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多