简体   繁体   中英

Entity Framework Code First Existing Database set EntityState.Unchanged on Many-to-Many

I have an existing database I'm coding against with Entity Framework 6 Code First. I have a many-to-many relationship that is working for Selects, Inserts and Deletes. I'm having an issue with EF adding an extra insert to the many-to-many table for an existing relationship.

Schema: 在此输入图像描述

DbContext:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
       Database.SetInitializer<MainContext>(null);

       modelBuilder.Entity<DocumentType>()
                .HasMany(u => u.DocumentStatuses)
                .WithMany()
                .Map(m =>
                {
                    m.MapLeftKey("DOCUMENT_TYPE_ID");
                    m.MapRightKey("DOCUMENT_STATUS_ID");
                    m.ToTable("DOCUMENT_TYPES_DOCUMENT_STATUSES");
                });

       base.OnModelCreating(modelBuilder);
    } 

DTOs:

    [Table("DOCUMENT_TYPES")]
    public class DocumentType
    {
        [Key]
        [Column("DOCUMENT_TYPE_ID")]
        public int? Id { get; set; }

        [Required]
        [Column("TYPE_NAME")]
        public string TypeName { get; set; }

        [Required]
        [Column("IS_ACTIVE")]
        public bool IsActive { get; set; }

        [Display(Name = "Updated By")]
        [Column("LAST_UPDATED_BY")]
        public string LastUpdatedBy { get; set; }

        [Display(Name = "Updated Date")]
        [Column("LAST_UPDATED_DATE")]
        public DateTimeOffset? LastUpdatedDate { get; set; }

        public virtual List<DocumentStatus> DocumentStatuses { get; set; } 

        public DocumentType()
        {
            DocumentStatuses = new List<DocumentStatus>();
        }
    }

[Table("DOCUMENT_STATUSES")]
public class DocumentStatus
{
    [Key]
    [Column("DOCUMENT_STATUS_ID")]
    public int? Id { get; set; }

    [Required]
    [Column("STATUS_NAME")]
    public string StatusName { get; set; }

    [Required]
    [Column("IS_COMPLETE")]
    public bool IsComplete { get; set; }

    [Required]
    [Column("IS_ACTIVE")]
    public bool IsActive { get; set; }

    [Display(Name = "Updated By")]
    [Column("LAST_UPDATED_BY")]
    public string LastUpdatedBy { get; set; }

    [Display(Name = "Updated Date")]
    [Column("LAST_UPDATED_DATE")]
    public DateTimeOffset? LastUpdatedDate { get; set; }
}

Repository Update:

    public bool Update(DocumentType entity, string updatedBy)
            {
                DateTimeOffset updatedDateTime = DateTimeOffset.Now;

                entity.LastUpdatedBy = updatedBy;
                entity.LastUpdatedDate = updatedDateTime;

                using (var db = new MainContext())
                {
                    db.DocumentTypes.Add((DocumentType)entity);
                    db.Entry(entity).State = EntityState.Modified;

                    foreach (var item in entity.DocumentStatuses)
                    {
                        if (item.Id != null)
                            db.Entry(item).State = EntityState.Unchanged;
                    }

                    db.SaveChanges();
                }

                return true;
            }

The loop that contains:

    if (item.Id != null)
     db.Entry(item).State = EntityState.Unchanged;

Prevents new records from being added, but EF still tries to insert into DOCUMENT_TYPES_DOCUMENT_STATUSES a duplicate of the existing many-to-many records.

Unit Test:

        [TestMethod()]
        public void UpdateTest()
        {
            DocumentTypeRepository documentTypeRepository = new DocumentTypeRepository();
            DocumentType type = NewDocumentType(true);
            DocumentType typefromDb;
            string updatedBy = "DocumentTypeRepositoryTests.UpdateTest";
            bool actual;

            type.IsActive = true;
            type.TypeName = RandomValues.RandomString(18);
            type.DocumentStatuses.Add(DocumentStatusRepositoryTests.NewDocumentStatus(true));

            actual = documentTypeRepository.Update(type, updatedBy);
            Assert.AreEqual(true, actual);

            typefromDb = documentTypeRepository.GetById((int)type.Id);
            Assert.AreEqual(type.DocumentStatuses.Count, typefromDb.DocumentStatuses.Count);
        }

How can I set the many-to-many table to EntityState.Unchanged when it already exists?

Try replacing

db.DocumentTypes.Add((DocumentType)entity);
db.Entry(entity).State = EntityState.Modified;

with

db.Entry(entity).State = entity.Id == null 
    ? EntityState.Added
    : EntityState.Modified;

When doing CRUD operations with detached entities, it's easier not to use the DbSet operations and only manipulate entity state entries. At least, it leads to less errors.

Two things are important to note here:

  1. When you Add an entity to a context, the whole object graph owned by the entity is marked as Added .

  2. In any association this also marks the associations as new. However, while in a 1:n or 1:1 association the state of the association changes when the state of one of the ends changes, in many-to-many associations there is a part of the association that is hidden and that will always remain Added . (Under the hood a many-to-many association is a 1:n:1 association, the n part is not visible in your code).

So this statement...

db.DocumentTypes.Add(entity);

causes the associations to get the Added state, and the subsequent...

db.Entry(entity).State = EntityState.Modified;

doesn't change this any more.

You have to make sure that for an existing DocumentType the state is never changed to Added , which you can do by checking its Id values, same as you do for DocumentStatus .

Also, you have to change the way you set the state of DocumentStatuses :

foreach (var item in entity.DocumentStatuses)
{
    db.Entry(item).State = item.Id != null
                               ? EntityState.Unchanged
                               : EntityState.Added;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM