实体框架迁移 - 启用自动迁移以及新增的迁移

13

我在项目中使用了Entity Framework 4.3 Migrations。我想使用自动迁移,这样当我修改领域对象和上下文类时,运行项目时数据库会自动更新。到目前为止,我已经让它正常工作。

除了自动迁移外,我还想使用一些Added Migrations,并且希望应用程序在运行时自动跳转到最新版本(基于我的添加的迁移)。

为了做到这一点,我在global.asax文件里加入了以下内容...

Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, Core.Migrations.Configuration>());

现在这样可以工作,但当我这样做时,它不再根据我的域对象自动更新数据库。

我希望能够完全删除数据库,然后运行应用程序,使所有自动迁移运行,然后运行我的显式迁移并将数据库升级到最新版本。

我知道我在以前的项目中已经做到了这一点,但我不确定在这种情况下我做错了什么。

谢谢

6个回答

12

在构造函数中传递一个启用AutomaticMigrationsEnabled选项的配置。像这样的内容应该会有所帮助:


Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyConfiguration>());

假设MyConfiguration的格式如下:


public class MyConfiguration : Core.Migrations.Configuration
{
    public MyConfiguration { this.AutomaticMigrationsEnabled = true; }
}

DISCLAIMER: Just hacked this in, so small tweaks might be required to get this to compile

EDIT:

Just checked with EF 4.3.1 and the code is like this for the initializer:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<DataContext, MyConfiguration>());

而这个是针对配置类的:

public class MyConfiguration : System.Data.Entity.Migrations.DbMigrationsConfiguration<DataContext>
{
    public MyConfiguration()
    {
        this.AutomaticMigrationsEnabled = true;
    }
}

不幸的是,我已经有了所有必要的东西。不幸的是,因为我已经在做了,但它并没有起作用。 - jdavis
同样的问题在这里,但是它完全没有任何作用。如果我在模型类中添加一个新属性,它不会被添加到相应的表中。 - Jerther
它运行得一般般。我需要添加 AutomaticMigrationDataLossAllowed = true; 以便进行某些迁移。这对于实时环境来说并不安全。 - Taurib
这正是我正在做的事情,但它什么也没做。 - Zimano

7

在这个问题上,我苦思冥想了好几个小时,最终找到了一个解决方案:如果需要,就创建数据库;如果过时,则升级。我们在Gallery Server Pro中使用这种技术,以便第一次安装或升级之前的版本更加容易。

private static void InitializeDataStore()
{
  System.Data.Entity.Database.SetInitializer(new System.Data.Entity.MigrateDatabaseToLatestVersion<GalleryDb, GalleryDbMigrationConfiguration>());

  var configuration = new GalleryDbMigrationConfiguration();
  var migrator = new System.Data.Entity.Migrations.DbMigrator(configuration);
  if (migrator.GetPendingMigrations().Any())
  {
    migrator.Update();
  }
}

public sealed class GalleryDbMigrationConfiguration : DbMigrationsConfiguration<GalleryDb>
{
  protected override void Seed(GalleryDb ctx)
  {
    MigrateController.ApplyDbUpdates();
  }
}

我写了一篇博客文章,详细介绍了以下内容: 使用Entity Framework Code First Migrations自动创建和更新应用程序


1
更新的链接:http://galleryserverpro.blogspot.ca/2013/10/using-entity-framework-code-first.html - Jerther
System.Data.Entity.Migrations.DbMigrator正是我所需要的。无需在NuGet PMC中运行命令,只需像在部署新版本到除开发机之外的任何地方一样,在C#中运行即可。 - Mike de Klerk
MigrateController 是什么? - MrGadget
MigrateController是一个自定义类,如果表为空,则插入种子数据并处理任何所需的模式/数据更改。通过查看Gallery Server源代码的示例来了解详情。您可以在https://galleryserverpro.com/release-history/下载它(搜索页面以获取源代码,您将看到获取3.2.1源代码的链接)。 - Roger
我们还可以在dbcontext上拥有一个静态构造函数。 - alhpe
这个版本适合我们,因为我们不想启用自动迁移。 - James Gray

2

使用DbContext上的静态构造函数实现与Roger相同的解决方案。完整代码如下...这样可以使初始化代码存在于类本身,并且在第一次实例化DataDbContext类时自动调用。

public partial class DataDbContext : DbContext
{
    public DataDbContext()
        : base("name=DefaultConnection")
    {
    }

    static DataDbContext() // This is an enhancement to Roger's answer
    {
        Database.SetInitializer(new DataDbInitializer()); 

        var configuration = new DataDbConfiguration();
        var migrator = new DbMigrator(configuration);

        if (migrator.GetPendingMigrations().Any())
            migrator.Update();
    }

    // DbSet's
    public DbSet<CountryRegion> CountryRegion { get; set; }
    // bla bla bla.....

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
        modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

        Configuration.ProxyCreationEnabled = false;
        Configuration.LazyLoadingEnabled = false;
        //Configuration.ValidateOnSaveEnabled = false; 

        base.OnModelCreating(modelBuilder);

        modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly()); // Discover and apply all EntityTypeConfiguration<TEntity> of this assembly, it will discover (*)
    }

}

internal sealed class DataDbInitializer : MigrateDatabaseToLatestVersion<DataDbContext, DataDbConfiguration>
{
}


internal sealed class DataDbConfiguration : DbMigrationsConfiguration<DataDbContext>
{
    public DataDbConfiguration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;
    }

    protected override void Seed(DataDbContext context)
    {
        DataSeedInitializer.Seed(context); 
        base.Seed(context);
    }
}

internal static class DataSeedInitializer
{
    public static void Seed(DataDbContext context)
    {
        SeedCountryRegion.Seed(context);
        // bla bla bla.....

        context.SaveChanges();
    }
}

internal static class SeedCountryRegion
{
    public static void Seed(DataDbContext context)
    {
        context.CountryRegion.AddOrUpdate(countryRegion => countryRegion.Id,

            new CountryRegion { Id = "AF", Name = "Afghanistan" },
            new CountryRegion { Id = "AL", Name = "Albania" },
            // bla bla bla.....

            new CountryRegion { Id = "ZW", Name = "Zimbabwe" });

        context.SaveChanges();
    }
}

public class CountryRegionConfiguration : EntityTypeConfiguration<CountryRegion> // (*) Discovered by
{
    public CountryRegionConfiguration()
    {
        Property(e => e.Id)
            .IsRequired()
            .HasMaxLength(3);

        Property(e => e.Name)
            .IsRequired()
            .HasMaxLength(50);
    }
}

public partial class CountryRegion : IEntity<string>
{
    // Primary key 
    public string Id { get; set; }

    public string Name { get; set; }

}

public abstract class Entity<T> : IEntity<T>
{
    //Primary key
    public abstract T Id { get; set; }
}

public interface IEntity<T>
{
    T Id { get; set; }
}

我们可以看到Seed方法一遍又一遍地运行。我们可以通过检查是否已存在迁移来避免这种情况,因为在创建数据库时会自动应用一个迁移。然后我们可以将DataDbConfiguration重构如下...

internal sealed class DataDbConfiguration : DbMigrationsConfiguration<DataDbContext>
{
    private readonly bool _isInitialized;

    public DataDbConfiguration()
    {
        AutomaticMigrationsEnabled = true;
        AutomaticMigrationDataLossAllowed = true;

        var migrator = new DbMigrator(this);

        _isInitialized = migrator.GetDatabaseMigrations().Any();
    }

    protected override void Seed(DataDbContext context)
    {
        InitializeDatabase(context);
    }

    public void InitializeDatabase(DataDbContext context)
    {

        if (!_isInitialized)
        {
            if (context.Database.Connection.ConnectionString.Contains("localdb"))
            {
                DataSeedInitializer.Seed(context); // Seed Initial Test Data
            }
            else
            {
                // Do Seed Initial Production Data here
            }

        }
        else
        {
            // Do any recurrent Seed here
        }
    }
}

1

这是我的当前解决方案,但我并不完全满意。

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    var context = new KCSoccerDataContext();
    var initializeDomain = new CreateDatabaseIfNotExists<KCSoccerDataContext>();
    var initializeMigrations = new MigrateDatabaseToLatestVersion<KCSoccerDataContext, Core.Migrations.Configuration>();

    initializeDomain.InitializeDatabase(context);
    initializeMigrations.InitializeDatabase(context);

}

我实际上正在创建两个不同的初始化程序。第一个使用CreateDatabaseIfNotExists,成功地根据我的域对象创建表。第二个使用MigrateDatabaseToLatestVersion,执行所有显式迁移。

我不喜欢它,因为自动迁移基本上被禁用了。因此,为了添加或更改我的域模型,我必须完全删除数据库并重新创建它。一旦我将应用程序移动到生产环境中,这是不可接受的。


0
如果您的应用程序包含Startup.cs类,您可以使用DbMigrator类如下 转到App_Start文件夹,打开Startup.Auth 将以下代码行粘贴到ConfigureAuth方法内部
var configuration = new Migrations.Configuration();
        var dbmigrator = new DbMigrator(configuration);
        dbmigrator.Update();

注意:记得使用这个命名空间- using System.Data.Entity.Migrations;
这样做的作用是在应用程序启动时将数据库更新到最新版本。

-1

你只需要做

    private static void InitializeDataStore()
    {
        System.Data.Entity.Database.SetInitializer(new System.Data.Entity.MigrateDatabaseToLatestVersion<GalleryDb, GalleryDbMigrationConfiguration>());
        System.Data.Entity.Database.Initialize(false);
    }

1
我看不出这如何回答问题。 - Arigion
语法也有问题,System.Data.Entity.Database.Initialize不是静态方法。 - QAZZY

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接