实体框架 - 迁移 - 代码优先 - 每次迁移播种

Tal*_*lon 40 c# entity-framework seeding .net-4.5 ef-migrations

我正在研究迁移,以便清理我们的部署过程.将更改推向生产时所需的人工干预越少越好.

我在迁移系统中遇到了3个主要障碍.如果我无法找到一个干净的方式,他们是显示塞子.

1.如何为每次迁移添加种子数据:

我执行命令"add-migration",它使用Up和Down函数来构建一个新的迁移文件.现在,我想通过Up和Down更改自动更改数据.我不希望将种子数据添加到Configuration.Seed方法,因为它运行所有以各种重复问题结束的迁移.

2.如果无法满足上述要求,我该如何避免重复?

我有一个枚举,我循环,以将值添加到数据库.

foreach(var enumValue in Enum.GetValues(typeof(Access.Level)))
{
    context.Access.AddOrUpdate(
        new Access { AccessId = ((int)enumValue), Name = enumValue.ToString() }
    );
}
context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

即使我使用的是AddOrUpdate,我仍然会在数据库中获得重复项.上面的代码将我带到了第三个也是最后一个问题:

3.如何为主键提供种子?

我用以上代码列举的是:

public class Access
{
    public enum Level
    {
        None = 10,
        Read = 20,
        ReadWrite = 30
    }
    public int AccessId { get; set; }
    public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我指定了我想要的值作为我的主键,但实体框架似乎忽略了它.他们最终仍然是1,2,3.我怎么能得到10,20,30?

目前这些EF的局限性还是有意限制,以防止我没有看到的其他类型的灾难?

Col*_*lin 28

  1. 当我想要通过迁移插入固定数据时,我使用调用将插入直接放入Up()迁移中Sql("Insert ...").请参阅本页中间的注释:如何插入固定数据
  2. 您可以通过调用AddOrUpdate重载来防止Seed方法中的重复项,该重载采用指定自然键的标识符表达式 - 请参阅此答案此博客条目.
  3. 默认情况下,作为整数的主键创建为标识字段.要另行指定,请使用该[DatabaseGenerated(DatabaseGeneratedOption.None)]属性

我认为这是初始化器和种子方法的一个很好的解释

以下是如何使用AddOrUpdate方法的示例:

foreach(var enumValue in Enum.GetValues(typeof(Access.Level)))
{
    context.Access.AddOrUpdate(
        x => x.Name, //the natural key is "Name"
        new Access { AccessId = ((int)enumValue), Name = enumValue.ToString() }
    );
}
Run Code Online (Sandbox Code Playgroud)


Gui*_*ino 13

作为一个可能的解决方案项目1,我做了一个实现IDatabaseInitializer,这将运行每个挂起的迁移种子法而已,你将需要实现自定义的策略,IMigrationSeed在每个接口DbMigration类,该Seed方法将被正确后执行Up,并Down每个迁移类的方法.

这有助于为我解决两个问题:

  1. 使用数据库数据迁移(或种子)进行组数据库模型迁移
  2. 检查Seed迁移代码应该在哪个部分运行,而不是检查数据库中的数据,而是使用已知数据,这些数据是刚刚创建的数据库模型.

界面看起来像这样

public interface IMigrationSeed<TContext>
{
    void Seed(TContext context);
}
Run Code Online (Sandbox Code Playgroud)

下面是将调用此Seed方法的新实现

public class CheckAndMigrateDatabaseToLatestVersion<TContext, TMigrationsConfiguration>
    : IDatabaseInitializer<TContext>
    where TContext : DbContext
    where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new()
{
    public virtual void InitializeDatabase(TContext context)
    {
        var migratorBase = ((MigratorBase)new DbMigrator(Activator.CreateInstance<TMigrationsConfiguration>()));

        var pendingMigrations = migratorBase.GetPendingMigrations().ToArray();
        if (pendingMigrations.Any()) // Is there anything to migrate?
        {
            // Applying all migrations
            migratorBase.Update();
            // Here all migrations are applied

            foreach (var pendingMigration in pendingMigrations)
            {
                var migrationName = pendingMigration.Substring(pendingMigration.IndexOf('_') + 1);
                var t = typeof(TMigrationsConfiguration).Assembly.GetType(
                    typeof(TMigrationsConfiguration).Namespace + "." + migrationName);

                if (t != null 
                   && t.GetInterfaces().Any(x => x.IsGenericType 
                      && x.GetGenericTypeDefinition() == typeof(IMigrationSeed<>)))
                {
                    // Apply migration seed
                    var seedMigration = (IMigrationSeed<TContext>)Activator.CreateInstance(t);
                    seedMigration.Seed(context);
                    context.SaveChanges();
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这里的好处是你有一个真正的EF上下文来操作种子数据,就像标准的EF种子实现一样.但是,如果您决定删除先前迁移中的种子表,则可能会出现这种情况,您必须相应地重构现有的种子代码.

编辑:作为在Up和Down之后实现种子方法的替代方法,您可以创建相同Migration类的部分类,我发现这很有用,因为它允许我在我想重新播种时安全地删除迁移类同样的迁移.

  • 这是天才!!!您需要更多方式。我所做的唯一更改是尝试/最后围绕更新进行了更改,因此,如果一次迁移失败,种子将继续。同样在Update之后,调用GetDatabaseTransaction()并将其与挂起进行比较,以便只有成功的迁移才会播种。也将Seed调用包装在它自己的事务中(同样,以防万一失败)。 (2认同)