Entity Framework Migrations - включить автоматическое перемещение вместе с добавлением миграции

Я использую Entity Framework 4.3 Миграции в моем проекте. Я хотел бы использовать автоматические миграции, чтобы при изменении моих объектов домена и моего класса контекста моя база данных автоматически обновлялась при запуске проекта. Я работаю до сих пор.

Я также хотел бы использовать некоторые Добавленные Миграции в дополнение к автоматическим переходам, и я хотел бы, чтобы приложение автоматически переходило к последней версии (основанной на моих добавленных миграциях) при запуске приложения.

Чтобы сделать это, я поместил это в файл global.asax...

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

Теперь это работает, но когда я это делаю, он больше не автоматически обновляет базу данных на основе моих объектов домена.

Я хотел бы иметь возможность полностью удалить базу данных, а затем запустить приложение и запустить все автоматические миграции, а затем выполнить мои явные миграции и довести базу данных до последней версии.

Я знаю, что у меня это работало в предыдущем проекте, но я не уверен, что я делаю неправильно в этом случае.

благодаря

Ответ 1

Вам нужно передать конфигурацию с параметром AutomaticMigrationsEnabled, установленным в конструктор true. Что-то вроде этого должно помочь:


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

с MyConfiguration будет что-то вроде:


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

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: просто взломали это, поэтому для его компиляции потребовались небольшие настройки

РЕДАКТИРОВАТЬ:

Просто проверен с помощью EF 4.3.1, и код будет таким же, как и для инициализатора:

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

и это для класса конфигурации:

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

Ответ 2

После нескольких ударов головой об этом, я наконец придумал решение, которое создает базу данных, если это необходимо, или обновляет ее, если она устарела. Мы используем этот метод в 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 для автоматического создания и автоматического обновления приложения

Ответ 3

Вот мое текущее решение, которое я не вполне доволен.

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, успешно проходит и создает таблицы на основе моих объектов Domain. Второй, используя MigrateDatabaseToLatestVersion, выполняет все мои явные миграции.

Мне это не нравится, потому что автоматическая миграция в основном отключена. Поэтому, чтобы добавить или изменить мою модель домена, я должен полностью отказаться от базы данных и воссоздать ее. Это будет неприемлемо, если я переведу приложение на производство.

Ответ 4

Вам просто нужно сделать

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

Ответ 5

Если ваше приложение содержит класс Startup.cs, вы можете использовать класс DbMigrator следующим образом. Перейдите в свою папку App_Start, откройте Startup.Auth. Вставьте эти строки кода внутри метода ConfigureAuth

var configuration = new Migrations.Configuration();
        var dbmigrator = new DbMigrator(configuration);
        dbmigrator.Update();

ПРИМЕЧАНИЕ. Не забудьте использовать этот namespace-, используя System.Data.Entity.Migrations;

это делается для обновления базы данных до последней версии в любое время, когда приложение запускается

Ответ 6

Такое же решение, которое сделал Роджер, но используя статический конструктор в DbContext. Полный код ниже.... это позволяет коду инициализации жить в самом классе и самозапускается при первом экземпляре класса DataDbContext.

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

    static DataDbContext() // This is an enhancement to Roger 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
        }
    }
}