简体   繁体   English

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

[英]Entity Framework - Migrations - Code First - Seeding per Migration

I am looking into Migrations in an effort to clean up our deployment processes. 我正在研究迁移,以便清理我们的部署过程。 The less manual intervention required when pushing a change to production the better. 将更改推向生产时所需的人工干预越少越好。

I have run into 3 major snags with the migrations system. 我在迁移系统中遇到了3个主要障碍。 They are show stoppers if I can not figure out a clean way around them. 如果我无法找到一个干净的方式,他们是show stoppers。

1. How do I add Seed data per migration: 1.如何为每次迁移添加种子数据:

I execute the command "add-migration" which scaffolds a new migration file with Up and Down functions. 我执行命令“add-migration”,它使用Up和Down函数来构建一个新的迁移文件。 Now, I want to automatically make changes to the data with both Up and Down changes. 现在,我想通过Up和Down更改自动更改数据。 I don't want to add the Seed data to the Configuration.Seed method as this runs for all migrations which ends in all sorts of duplication problems. 我不希望将种子数据添加到Configuration.Seed方法,因为它运行所有以各种重复问题结束的迁移。

2. If the above is not possible, how do I avoid duplications? 2.如果无法满足上述要求,我该如何避免重复?

I have an enum that I loop through to add the values to the database. 我有一个枚举,我循环,以将值添加到数据库。

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

Even though I am using AddOrUpdate, I still get duplicates in the database. 即使我使用的是AddOrUpdate,我仍然会在数据库中获得重复项。 The above code brings me to my 3rd and final problem: 上面的代码将我带到了第三个也是最后一个问题:

3. How can I seed Primary Keys? 3.如何为主键提供种子?

My enumerable with the above code is: 我用以上代码列举的是:

public class Access
{
    public enum Level
    {
        None = 10,
        Read = 20,
        ReadWrite = 30
    }
    public int AccessId { get; set; }
    public string Name { get; set; }
}

I am specifying the values that I want as my primary key, but Entity Framework seems to ignore it. 我指定了我想要的值作为我的主键,但实体框架似乎忽略了它。 They still end up being 1,2,3. 他们最终仍然是1,2,3。 How do I get it to be 10,20,30? 我怎么能得到10,20,30?

Are these limitations of EF at the moment or are they intentional constraints to prevent some other kind of catastrophe I am not seeing? 目前这些EF的局限性还是有意限制,以防止我没有看到的其他类型的灾难?

  1. When I have fixed data that I want to insert with a migration, I put the inserts directly in the Up() migration using calls to Sql("Insert ...") . 当我想要通过迁移插入固定数据时,我使用对Sql("Insert ...")调用Sql("Insert ...")将插入直接放入Up()迁移中。 See the note halfway down this page: how to insert fixed data 请参阅本页中间的注释: 如何插入固定数据
  2. You prevent duplicates in the Seed method by calling the AddOrUpdate overload that takes an identifier expression specifying the natural key - see this answer and this blog entry . 您可以通过调用AddOrUpdate重载来防止Seed方法中的重复项,该重载采用指定自然键的标识符表达式 - 请参阅此答案此博客条目
  3. Primary keys that are integers are created as identity fields by default. 默认情况下,作为整数的主键创建为标识字段。 To specify otherwise use the [DatabaseGenerated(DatabaseGeneratedOption.None)] attribute 要另行指定,请使用[DatabaseGenerated(DatabaseGeneratedOption.None)]属性

I think this is a good explanation of Initializer and Seed methods 我认为这是初始化器和种子方法的一个很好的解释

Here is an example of how to use the AddOrUpdate method: 以下是如何使用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() }
    );
}

As a possible solution to item 1, I made an implementation of the IDatabaseInitializer strategy which will run the Seed method of each pending migration only, you will need to implement a custom IMigrationSeed interface in each of your DbMigration classes, the Seed method will then be implemented right after Up and Down methods of every migration class. 作为第1项的可能解决方案,我实现了IDatabaseInitializer策略,该策略仅运行每个待处理迁移的Seed方法,您需要在每个DbMigration类中实现自定义IMigrationSeed接口,然后Seed方法将是在每个迁移类的UpDown方法之后Up实现。

This helps to solve two problems for me: 这有助于为我解决两个问题:

  1. Group Database Model Migration with Database Data Migration (or Seeding) 使用数据库数据迁移(或种子)进行组数据库模型迁移
  2. Check what part of the Seed migration code should really be running, not checking data in the database but using already known data which is the database model that was just created. 检查Seed迁移代码应该在哪个部分运行,而不是检查数据库中的数据,而是使用已知数据,这些数据是刚刚创建的数据库模型。

The interface looks like this 界面看起来像这样

public interface IMigrationSeed<TContext>
{
    void Seed(TContext context);
}

Below is the new implementation that will call this Seed method 下面是将调用此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();
                }
            }
        }
    }
}

The good thing here is you have a real EF context to manipulate Seed Data, just like standard EF Seed implementation. 这里的好处是你有一个真正的EF上下文来操作种子数据,就像标准的EF种子实现一样。 However this can get strange if for example you decide to delete a table that was Seeded in a previous migration, you will have to refactor your existing Seed code accordingly. 但是,如果您决定删除先前迁移中的种子表,则可能会出现这种情况,您必须相应地重构现有的种子代码。

EDIT: As an alternative to implement the seed method after the Up and Down, you can create a partial class of the same Migration class, I found this useful as it allows me to safely delete the migration class when I want to re-seed the same migration. 编辑:作为在Up和Down之后实现种子方法的替代方法,您可以创建相同Migration类的部分类,我发现这很有用,因为它允许我在我想重新播种时安全地删除迁移类同样的迁移。

Hi I have found a very useful information for your problem in this link: Safari Books Online 您好我在此链接中找到了有关您的问题的非常有用的信息: Safari Books Online

"1. How do I add Seed data per migration:" As you see in the example you need to create a new confiugration for seeding. “1.如何为每次迁移添加种子数据:”正如您在示例中所看到的,您需要为种子创建新的混淆。 This seed Configuration must be called after migration. 迁移后必须调用此种子配置。

public sealed class Configuration : DbMigrationsConfiguration
{
    public Configuration()
    {
        AutomaticMigrationsEnabled = false;
    }

    protected override void Seed(SafariCodeFirst.SeminarContext context)
    {
        //  This method will be called after migrating to the latest version.

        //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
        //  to avoid creating duplicate seed data. E.g.
        //
        //    context.People.AddOrUpdate(
        //      p => p.FullName,
        //      new Person { FullName = "Andrew Peters" },
        //      new Person { FullName = "Brice Lambson" },
        //      new Person { FullName = "Rowan Miller" }
        //    );
        //
    }
}

"2. If the above is not possible, how do I avoid duplications?" “2.如果上述情况不可能,我该如何避免重复?”

AddOrUpdate Must help you exactly to avoding the duplicates if you get an error here you might have a configuration error post the call stack please. AddOrUpdate如果你在这里收到错误,必须帮助你准确地复制重复项,你可能会在调用堆栈后发生配置错误。 See the example! 看看这个例子!

"3. How can I seed Primary Keys?" “3.我怎样才能为主键提供种子?”

Here it is also on your key definition. 这也是你的关键定义。 If your key DatabaseGenerated(DatabaseGeneratedOption.Identity) than you do not have to provide it. 如果您的密钥DatabaseGenerated(DatabaseGeneratedOption.Identity)比您不必提供它。 In some other senarios you need to create a new one it is depending on the key type. 在其他一些senarios中,您需要创建一个新的,取决于密钥类型。

"Are these limitations of EF at the moment or are they intentional constraints to prevent some other kind of catastrophe I am not seeing?" “目前EF的这些限制还是他们故意限制,以防止我看不到其他类型的灾难?”
Not that I know! 不是我知道的!

OK, so with a bit of bashing I have managed to bash EF into submission. 好吧,所以有点抨击我已经设法将EF击败提交。 Here is what I did: 这是我做的:

1. There is no way that I found to see data for a specific migration. 1.我找不到特定迁移的数据。 It all must go into the common Configuration.Seed method. 这一切都必须进入常见的Configuration.Seed方法。

2. To avoid duplicates I had to do 2 things. 2.为了避免重复,我不得不做两件事情。 For my enums, I wrote the following seed code: 对于我的枚举,我写了以下种子代码:

foreach (var enumValue in Enum.GetValues(typeof(Access.Level)))
{
    var id = (int)enumValue;
    var val = enumValue.ToString();

    if(!context.Access.Any(e => e.AccessId == id))
        context.Access.Add(
            new Access { AccessId = id, Name = val }
        );
}
context.SaveChanges();

So basically, just checking if it exists and adding if not 所以基本上,只检查它是否存在,如果不存在则添加

3. In order for the above to work, you need to be able to insert Primary Key Values. 3.为了使上述功能正常工作,您需要能够插入主键值。 Luckily for me this table will always have the same static data so I could deactivate the auto increment. 幸运的是,这个表将始终具有相同的静态数据,因此我可以取消激活自动增量。 To do that, the code looks like: 为此,代码如下所示:

public class Access
{
    public enum Level
    {
        None = 10,
        Read = 20,
        ReadWrite = 30
    }

    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int AccessId { get; set; }
    public string Name { get; set; }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM