Search code examples
c#entity-frameworkentity-framework-6code-firstentity-framework-migrations

Entity Framework 6.1.3 initialize existing, but empty database from code-first model


I've got some Problems while preparing an Application for the release on production systems.

The situation:

  • Entity Framework 6.1.3 Code-First Model with several migrations
  • A windows service with dedicated serviceuser (not Local System)
  • The configuration (handled by a WPF-App) allows to paste a connectionstring pointing to the desired MS-SQL DB
  • The DB should be prepared by the installing admin, so that the serviceuser has dbowner, dbwriter and dbreader rights. (should be enough for future migrations)
  • The serviceuser should not have rights on the SQL-Servers masterdb. That's why the desired DB for the windows service must be created by the admin before starting the service

What i'm trying to do:

  • Writing an IDatabaseInitializer<MyDbContext>, which initializes the existing, but empty database upon detection AND updates the database in case of an outdated model

What I tried:

  • Using the MigrateDatabaseToLatestVersion initializer => This one fails since there is no __MigrationHistory table in the prepared, but empty database

  • Using the CreateDatabaseIfNotExists initializer => This one fails, since the database is already existing, whicht leeds this initializer to do ... nothing

  • Writing my own IDbInitializer with the following InitializeDatabase-method:

    public void InitializeDatabase(RapasiDbContext context)
    {
        if (!context.Database.Exists()) //Shouldn't happen
        {                
          ... // Throw Execption: No prepared DB found
        }           
        try //Initialize the existing database, but only if not already done (force:false)
        {
            context.Database.InitializeDatabase(false);
        }
        catch (Exception ex)
        {
            Log.Fatal("Error while initializing the database", ex);
        }           
        try //Update the databse, if model is outdated
        {
            var dbMigrationConfig = new Configuration();
            if (dbMigrationConfig.AutomaticMigrationsEnabled && !context.Database.CompatibleWithModel(true))
            {
                Log.Info("database schame outdated, starting migration!");
                try
                {
                    var migrator = new DbMigrator(dbMigrationConfig);
                    migrator.Update();
                }
                catch (Exception ex)
                {
                    Log.Fatal("Error while migrating the database", ex);
                }
            }
        }
        catch (Exception ex)
        {
            Log.Fatal("No Migration-Histroy found!", ex);
        }
    }
    

The problem(s)

  • In the way I'm trying it now, the Initializer it not called at all
  • Switching to InitializeDatabase(force:true) leeds to recursivly calling my own InitializeDatabase method :(

The question:

How is it possible, to initialize an exsiting database with the InitialCommit-Up() script from my migrations?

Or am I missing some really obvious point, why this kind of DB initialization is not meant to happen? Are there any best practices for my situation I just don't know?


Solution

  • MigrateDatabaseToLatestVersion initializer should work, even if there is no __MigrationHistory table. It is supposed to be created when the migrations are run for the first time.

    As you say in your comment in the original question, you need to provide the right values for the constructor parameters of MigrateDatabaseToLatestVersion when calling SetInitializer method:

    Database.SetInitializer(new MigrateDatabaseToLatestVersion<RapasiDbContext, Configuration>(true, new Configuration()));
    

    As the reference page says, the boolean parameter in the constructor specifies whether to use the connection information from the context that triggered initialization to perform the migration. If this parameter is missing the connection information will be obtained from a context constructed using the default constructor or registered factory (this may not be what you need).

    Also, I think you may be interpreting the AutomaticMigrations configuration incorrectly. It does not mean that your migrations will be APPLIED automatically, it means they will be GENERATED automatically if you didn't manually run Add-Migration after your model changed. I guess you want your pending migrations to be executed when your database is not up to date, even if AutomaticMigrations are disabled.