code

Entity Framework 코드 첫 번째 AddOrUpdate 메서드 삽입 중복 값

codestyles 2020. 12. 25. 09:51
반응형

Entity Framework 코드 첫 번째 AddOrUpdate 메서드 삽입 중복 값


간단한 엔티티가 있습니다.

public class Hall
{
    [Key]
    public int Id {get; set;}

    public string Name [get; set;}
}

그런 다음 테이블을 채우는 데 Seed사용 하는 방법 에서 AddOrUpdate:

var hall1 = new Hall { Name = "French" };
var hall2 = new Hall { Name = "German" };
var hall3 = new Hall { Name = "Japanese" };

context.Halls.AddOrUpdate(
    h => h.Name,
    hall1,
    hall2,
    hall3
);

그런 다음 패키지 관리 콘솔에서 실행합니다.

Add-Migration Current
Update-Database

괜찮습니다. "Hall"테이블에 3 개의 행이 있습니다. 그러나 패키지 관리 콘솔에서 Update-Database다시 실행하면 이미 5 개의 행이 있습니다.

Id  Name
1   French
2   Japaneese
3   German
4   French
5   Japanese

왜? 5 행이 아니라 3 행이어야한다고 생각합니다. Id대신 속성 을 사용하려고 시도했지만 Name차이가 없습니다.

최신 정보:

이 코드는 동일한 결과를 생성합니다.

var hall1 = new Hall { Id = 1, Name = "French" };
var hall2 = new Hall { Id = 2, Name = "German" };
var hall3 = new Hall { Id = 3, Name = "Japanese" };

context.Halls.AddOrUpdate(
                h => h.Id,
                hall1);

context.Halls.AddOrUpdate(
                h => h.Id,
                hall2);

context.Halls.AddOrUpdate(
                h => h.Id,
                hall3);

또한 너겟을 통해 최신 EntityFramework가 설치되어 있습니다.


좋아, 나는 이것으로 한 시간 동안 키보드에서 내 얼굴을 두들 겼다. 테이블의 Id 필드가 ID 필드이면 작동하지 않으므로 identifierExpression에 다른 필드를 사용하십시오. Name 속성을 사용하고 new Hall {...}이니셜 라이저 에서 Id 필드도 제거했습니다 .

OPs 코드에 대한이 조정은 나를 위해 일했기 때문에 누군가에게 도움이되기를 바랍니다.

protected override void Seed(HallContext context)
{
    context.Halls.AddOrUpdate(
        h => h.Name,   // Use Name (or some other unique field) instead of Id
        new Hall
        {
            Name = "Hall 1"
        },
        new Hall
        {
            Name = "Hall 2"
        });

    context.SaveChanges();
}

나는 이것이 오래된 질문이라는 것을 알고 있지만 정답은 ID #를 직접 설정하고 AddOrUpdate를 사용하려는 경우 EF / SQL에 ID # 생성을 원하지 않는다고 알려야한다는 것입니다.

modelBuilder.Entity<MyClass>().Property(p => p.Id)
    .HasDatabaseGeneratedOption(System.ComponentModel
    .DataAnnotations.Schema.DatabaseGeneratedOption.None); 

단점은 새 항목을 삽입 할 때 ID를 설정해야한다는 것입니다. 따라서 이것이 런타임에 동적으로 수행되면 (시드 데이터 대신) 다음 ID를 계산해야합니다. Context.MyClasses.Max(c=>c.Id) + 1잘 작동합니다.


이 코드는 다음과 같이 작동합니다.

public Configuration()
{
    AutomaticMigrationsEnabled = true;
}

protected override void Seed(HallContext context)
{
    context.Halls.AddOrUpdate(
        h => h.Id,
        new Hall
        {
            Id = 1,
            Name = "Hall 1"
        },
        new Hall
        {
            Id = 2,
            Name = "Hall 2"
        });

    context.SaveChanges();
}

This can also be caused if you're setting the Entity State incorrectly. I kept getting the following error when I'd run update-database..."Sequence contains more than one matching element."

For example, I had duplicate rows being created on each update-database command (which of course is not supposed to happen when seeding data), and then the next update-database command wouldn't work at all since it found more than one match (hence the sequence error saying I have more than one matching row). That's because I had overridden SaveChanges in my context file with a method call to ApplyStateChanges...

public override int SaveChanges()
{
    this.ApplyStateChanges();
    return base.SaveChanges();
}

I was using ApplyStateChanges to ensure that when adding object graphs, Entity Framework knows explicitly whether the object is in an added or modified state. The entire explanation on how I'm using ApplyStateChanges can be found here.

And this works great (but the caveat!!)...if you're also seeding the database using CodeFirst migrations, then the above method will cause havoc for the AddOrUpdate() call within the Seed Method. So before anything else, just check your DBContext file and ensure you're not overriding SaveChanges in the way above, or you will end up getting duplicate data running the update-database command a second time, and then won't work at all the third time since there's more than one row for each matching item.

When it comes down to it, you don't need to configure the Id in AddOrUpdate()...that defeats the whole purpose of easy and initial database seeding. It works fine by something like:

context.Students.AddOrUpdate(
    p => p.StudentName,
    new Student { StudentName = "Bill Peters" },
    new Student { StudentName = "Jandra Nancy" },
    new Student { StudentName = "Rowan Miller" },
    new Student { StudentName = "James O'Dalley" },

just AS LONG as I'm not overriding the SaveChanges method in my context file with a call to ApplyStateChanges. Hope this helps.


This worked for me

  1. Delete all the rows in the table.
  2. Reset the incremental identity to 0. DBCC CHECKIDENT (yourtablename, RESEED, 0) (The primary keys specified in the Seed() must match those in the database table so that they do not duplicate.)
  3. Specify the primary keys in the 'seed' method.
  4. Run the Seed() method several times and you check if they duplicated.

I have found that AddOrUpdate works fine with fields that are not ID's. If this works for you: context.Halls.AddOrUpdate(h => h.Name, hall1, hall2, hall3)

You may want to use Hall names like 'French_test_abc_100', 'German_test_abc_100' etc.

That stops hard coded test data messing things up when you are testing your app.


If object(hall)'s id is 0, it is a insertion. I think you need to double check the id field of your hall objects


Is your ID field an Identity field? I was running into this same issue. When I removed the Identity status from my ID field and set the IDs going into the database, that resolved the issue.

That worked for me, since these were look-up tables and shouldn't have been identity fields, anyway.


I think it's likely that you need to back out existing database migrations (i.e. start your database from scratch) with something like 'Update-Database TargetMigration:0' followed by 'Update-Database'.

As it is, you're not dropping the existing table or values, you're just add/updating those values. That needs to happen in order to get your desired result.

Here's a good reference for EF migrations: http://elegantcode.com/2012/04/12/entity-framework-migrations-tips/


I used the ID field as Identity/Key and add attributes not to assign Ids by the server. This solved the problem for me.

public class Hall
{
    [Key]
    [Required]
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id {get; set;}

    public string Name [get; set;}
 }

Just to Ciaren's answer, the below code of resetting the context on ModelCreating, helped me resolve similar issues. Make sure change "ApplicationContext" to your DbContext name.

public class ApplicationContext : DbContext, IDbContext
    {
        public ApplicationContext() : base("ApplicationContext")
        {
             
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
            Database.SetInitializer<ApplicationContext>(null);
            base.OnModelCreating(modelBuilder);
        }
     }


I found out that for this to work, the identity position should be 0 when the seed first run. You can reset it using:

DBCC CHECKIDENT (tableName, RESEED, 0)

You could have also done this:

 context.Halls.AddOrUpdate(new Hall[]{hall1,hall2, hall3});

ReferenceURL : https://stackoverflow.com/questions/10007351/entity-framework-code-first-addorupdate-method-insert-duplicate-values

반응형