Seeding data will not work when schema changes with Code First when using migrations

时间:2015-10-30 23:32:03

标签: c#-4.0 ef-code-first entity-framework-6 seeding

Okay so I am using Entity Framework 6.1 and attempting code first with seeding. I have a drop always intializer that ALWAYS WORKS. However I want to use database migration which I have set up and it works. UNTIL I normalize out a table and then try to seed it I get a primary key error. Basically in my context when I uncomment out the changes in the 'TODO' section and the schema changes I get a primary key violation when attempting population of the newly normalized out table. It will work for the initializer that does the drop always, but I want my migration data table and not to drop the database everytime I make changes in case I want to rollback ever. I have tried changing the attribute of the 'PersonId' to Identity and to None and back to Identity. So the caveat is if it is set to 'Identity' it will work but the values will keep incrementing to higher values each time 1,2,3,4 then 5,6,7,8;etc. If I set it to none it works the first time and then when it is split in the mapping and normalized it blows up. I have tried custom dbcc commands and it does not like that either, as even with setting dbcc to reseed with the two new tables it does not like it. It is as if it has no idea about seeding the new table when being done explicitly. Does anyone know how to do a seeding process that the model can handle if you normalize out the mapping of an object to multiple tables? I am trying a bunch of different patterns and getting nowhere fast. So POCO Object public class Person { [DatabaseGenerated(DatabaseGeneratedOption.None)] public int PersonId { get; set; } [Column(TypeName = "varchar")] [Required] [MaxLength(32)] public string FirstName { get; set; } [Column(TypeName = "varchar")] [Required] [MaxLength(32)] public string LastName { get; set; } [Column(TypeName = "varchar")] public string OverlyLongDescriptionField { get; set; } } Context for code First: public class EasyContext : DbContext { public EasyContext() : base("name=EasyEntity") { //Database.SetInitializer<EasyContext>(new EasyInitializer()); Database.SetInitializer(new MigrateDatabaseToLatestVersion<EasyContext, Migrations.Configuration>("EasyEntity")); } public DbSet<ProductOrder> ProductOrder { get; set; } public DbSet<Person> Person { get; set; } public DbSet<Product> Product { get; set; } public DbSet<Audit> Backup { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.HasDefaultSchema("dbo"); modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>(); //TODO Let's normalize out a long descriptive field //modelBuilder.Entity<Person>() //.Map(m => //{ // m.Properties(p => new { p.FirstName, p.LastName }); // m.ToTable("Person"); //}) //.Map(m => //{ // m.Properties(p => new { p.OverlyLongDescriptionField }); // m.ToTable("PersonDescription"); //}); } } Initializer for DropCreateAlways: public class EasyInitializer : DropCreateDatabaseAlways<EasyContext> { protected override void Seed(EasyContext context) { SeedingValues.SeedingForDatabaseDrop(context); base.Seed(context); } } Configuration for migrations: internal sealed class Configuration : DbMigrationsConfiguration<EasyEntity.EasyContext> { public Configuration() { AutomaticMigrationsEnabled = true; AutomaticMigrationDataLossAllowed = true; ContextKey = "EasyEntity.EasyContext"; } protected override void Seed(EasyContext context) { SeedingValues.SeedingWithoutDatabaseDrop(context); base.Seed(context); } } Base Seeding class: internal static class SeedingValues { public static void SeedingForDatabaseDrop(EasyContext context) { BaseSeed(context); } public static void SeedingWithoutDatabaseDrop(EasyContext context) { context.Person.ClearRange(); BaseSeed(context); } private static void BaseSeed(EasyContext context) { IList<Person> persons = new List<Person> { new Person { PersonId = 1, FirstName = "Brett", LastName = "Guy", OverlyLongDescriptionField = "OMG Look I have a bunch of text denormalizing a table by putting a bunch of stuff only side related to the primary table." }, new Person { PersonId = 2, FirstName = "Neil", LastName = "Person"}, new Person { PersonId = 3, FirstName = "Ryan", LastName = "Other"}, new Person { PersonId = 4, FirstName = "Aaron", LastName = "Dude"}, }; foreach (var person in persons) context.Person.AddOrUpdate(person); } } ClearingHelper public static void ClearRange<T>(this DbSet<T> dbSet) where T : class { using (var context = new EasyContext()) { dbSet.RemoveRange(dbSet); } }

1 个答案:

答案 0 :(得分:0)

好的,问题是新填充的表没有填充,而且正在填充旧表。所以如果我跟随我的例子POCO课程&#39; Person&#39;并去除了plurilization。没有任何显式映射,只需要一个DbSet就会创建一个表Person。如果我然后我的映射分割表。

modelBuilder.Entity<Person>()
.Map(m =>
{
    m.Properties(p => new { p.PersonId, p.FirstName, p.LastName });
    m.ToTable("Person");
})
.Map(m =>
{
    m.Properties(p => new { p.PersonId, p.OverlyLongDescriptionField });
    m.ToTable("PersonDescription");
});

我的播种过程中出现了主键违规行为。这是因为如果我查看新更新的数据库,它仍然保留旧表并创建一个新表。但是,它不知道如何使用此方法删除数据:

public static void ClearRange<T>(this DbSet<T> dbSet) where T : class
{
    using (var context = new EasyContext())
    {
        dbSet.RemoveRange(dbSet);
    }
}

设置是偏的。所以我在想:&#34;好吧,如果我的播种数据包含在这一点上,我需要改进它,我理论上可以直接用SQL命令清理表格。&#34;这不是我想说的方法,但确实有用。

所以我向清算助手添加了更多数据:

public static void ResetIdentity(string tableName)
{
    using (var context = new EasyContext())
    {
        context.Database.ExecuteSqlCommand($"DBCC CHECKIDENT('{tableName}', RESEED, 0)");
    }
}

public static void DeleteTable(string tableName)
{
    using (var context = new EasyContext())
    {
        context.Database.ExecuteSqlCommand($"DELETE {tableName}");
    }
}

public static void DeleteTableAndResetIdentity(string tableName)
{
    using (var context = new EasyContext())
    {
        context.Database.ExecuteSqlCommand($"DELETE {tableName}");
        context.Database.ExecuteSqlCommand($"DBCC CHECKIDENT('{tableName}', RESEED, 0)");
    }
}

然后我将此添加到我的种子例程的清理部分:

ClearingHelper.DeleteTable("dbo.PersonDescription");
ClearingHelper.DeleteTableAndResetIdentity("dbo.Person");

这很不幸以两种方式做到这一点:

  1. 逐行删除它的速度较慢。
  2. 如果我向后迁移,我将不得不改变它。
  3. 但它有效!我现在可以通过规范化POCO来更改模式,并且仍然可以运行播种程序。