0
votes

I have been experiencing the ObjectContext inconsistent state exception:

The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: AcceptChanges cannot continue because the object's key values conflict with another object in the ObjectStateManager. Make sure that the key values are unique before calling AcceptChanges.

This seems to happen when my base entity (abstract since using TPC) has an

public int Id { get; set;}

which is configured as a primary key generated by the database:

// Base Entity
modelBuilder.Entity<BaseEntityObject>().HasKey(t => t.Id);
modelBuilder.Entity<BaseEntityObject>().Property(t => t.Id).
    HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

and derived entities are also inheriting this and trying to use it as their primary key.

When you instantiate two different types of derived entries, add them to the DbContext, then try to SaveChanges(), the exception is fired. However, when the Id field is changed to a Guid from an int:

public Guid Id { get; set;}

this exception is no longer thrown.

  1. Can someone explain why this happens when the Id property is of type int?

  2. Is there a workaround to this? It seems a bit wasteful to have every single derived entity to have a Guid PK.

1
can you post an example of your base and one of the child classes? Also, is your ID column, marked as IsIdentity in the editor? - Robert
I can definitely post an example of the base and one child class. What do you mean be "is your ID column, marked as IsIdentity in the editor"? - blgrnboy
Is your ID column marked as PrimaryKey and set to Identity column in database, and if it is, does it have EntityKey property set to true in the EDMX model editor? - Robert
This is a EF Code First approach, so I am actually not sure how to check the EDMX model editor. However, per the Fluent API, ID is a PK Field, and the tables created in the tables for the derived classes do indicate this as well. - blgrnboy
Actually, I just found something that is totally different in my DbContext configuration than here: weblogs.asp.net/manavi/…. It seems like they have only defined a DbSet for the Base Class, while I have a DbSet defined for every base class. Perhaps this is why I am getting the error? - blgrnboy

1 Answers

1
votes

I think problem you are facing is because you are configuring the Primary key and Identity for Base Entity but you are supposed to do same for your derived entity because in general we are not storing BaseEntityObject in database.

You need to do something like below:

modelBuilder.Entity<DerivedEntityObject1>().HasKey(t => t.Id);
modelBuilder.Entity<DerivedEntityObject1>().Property(t => t.Id).
    HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

modelBuilder.Entity<DerivedEntityObject2>().HasKey(t => t.Id);
modelBuilder.Entity<DerivedEntityObject2>().Property(t => t.Id).
    HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

Update:

  1. Create base Entity Type Configuration and inherit that in your entity configuration.

    public class TestContext : DbContext
    {
        public DbSet<TestEntity1> TestEntities1 { get; set; }
        public DbSet<TestEntity2> TestEntities2 { get; set; }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new TestEntity1Configuration());
            modelBuilder.Configurations.Add(new TestEntity2Configuration());
    
            base.OnModelCreating(modelBuilder);
        }
    }
    
    public class BaseEntityConfiguration<T> : EntityTypeConfiguration<T> where T : BaseEntity
    {
        public BaseEntityConfiguration()
        {
            HasKey(d => d.Id).Property(d => d.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
        }
    }
    
    public class TestEntity1Configuration : BaseEntityConfiguration<TestEntity1> { 
        //your configuration here.
    }
    
    public class TestEntity2Configuration : BaseEntityConfiguration<TestEntity2> {
        //your configuration here.
    }
    
  2. Smart way is to use data annotation like below:

    public class BaseEntity
    {
        [Key]
        [DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
    }
    
    public class TestEntity1 : BaseEntity
    {
        public string Name { get; set; }
    }
    
    public class TestEntity2 : BaseEntity
    {
        public string Name2 { get; set; }
    }