I have simple entity:
public class Hall
{
[Key]
public int Id {get; set;}
public string Name [get; set;}
}
Then in the Seed
method I use AddOrUpdate
to populate table:
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
);
Then I run in the Package Management Console:
Add-Migration Current
Update-Database
It's all fine: I have three rows in the table "Hall". But if I run in the Package Management Console Update-Database
again I have already five rows:
Id Name
1 French
2 Japaneese
3 German
4 French
5 Japanese
Why? I think it is should be three rows again, not five. I tried to use Id
property instead of Name
but it does not make the difference.
UPDATE:
This code produces the same result:
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);
Also I have the latest EntityFramework installed via nuget.
Ok I was banging my face off the keyboard for an hour with this. If your table's Id field is an Identity field then it won't work so use a different one for identifierExpression. I used the Name property and also removed the Id field from the new Hall {...}
initializer.
This tweak to the OPs code worked for me so I hope it helps someone:
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();
}
This code works:
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();
}
I know this is an old question, but the right answer is that if you are setting the id # yourself and you want to use AddOrUpdate then you need to tell EF/SQL that you don't want it to generate the ID #.
modelBuilder.Entity<MyClass>().Property(p => p.Id)
.HasDatabaseGeneratedOption(System.ComponentModel
.DataAnnotations.Schema.DatabaseGeneratedOption.None);
The down side to this is that when you insert a new item you need to set it's Id, so if this is done dynamically at runtime (instead of from seed data) then you will need to calculate out the next Id. Context.MyClasses.Max(c=>c.Id) + 1
works well.
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.
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.)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});
AddOrUpdate
for every record? It is quite strange that you have 5 records. It means that it worked once so there must be something special in your - Ladislav Mrnka 2012-04-04 08:34