在使用迁移时,当模式通过 Code First 更改时,播种数据将不起作用

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

好的,所以我正在使用 Entity Framework 6.1 并首先尝试通过播种编写代码。我有一个始终有效的 drop always 初始化器。但是我想使用我设置的数据库迁移并且它可以工作。直到我规范化 table 然后尝试为它播种我得到一个主键错误。

基本上在我的上下文中,当我取消注释 'TODO' 部分中的更改和模式更改时,我在尝试填充新规范化的 table 时遇到主键冲突。它适用于始终执行删除的初始化程序,但我希望我的迁移数据 table 而不是每次我进行更改时都删除数据库,以防我想回滚。我已尝试将 'PersonId' 的属性更改为 Identity,再更改为 None,然后再更改回 Identity。所以需要注意的是,如果它设置为 'Identity' 它会起作用,但每次 1、2、3、4 然后 5、6、7、8 等时,这些值都会增加到更高的值;等等。如果我将它设置为 none 它第一次工作,然后当它在映射中被分割并规范化时它就爆炸了。我已经尝试过自定义 dbcc 命令,但它也不喜欢那样,即使将 dbcc 设置为使用两个新的 tables 重新播种,它也不喜欢它。就好像它不知道在明确完成时播种新的 table。

如果您将一个对象的映射标准化为多个 table,有谁知道如何进行模型可以处理的播种过程?我正在尝试一堆不同的模式,但很快就一事无成。

所以 POCO 对象

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; }
}

代码上下文优先:

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");
            //});
        }
    }

DropCreateAlways 的初始化程序:

public class EasyInitializer : DropCreateDatabaseAlways<EasyContext>
    {
        protected override void Seed(EasyContext context)
        {
            SeedingValues.SeedingForDatabaseDrop(context);

            base.Seed(context);
        }
    }

迁移配置:

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);
        }
    }

基地播种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);
    }
}

清除助手

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

好的,所以问题在于新创建的 table 未被填充,而旧的 table 被填充。因此,如果我遵循我的示例 POCO class 'Person' 并删除了复数化。没有任何显式映射,仅 DbSet 将创建一个 table 人。如果我然后进行映射以拆分 tables.

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");
});

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

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

因为集合是部分的。所以我在想:"Well if my data for seeding is contained at this point and I need to alter it moving forward I can in theory just clean up the tables directly with SQL commands." 这不是我想要的方法,但它确实有效。

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

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. 如果我向后迁移,我将不得不更改它。

但它有效!我现在可以通过规范化 POCO 来更改模式,并且仍然是 运行 播种例程。