EF Code First MigrateDatabaseToLatestVersion accepts connection string Name from config

16,487

Solution 1

Thank you all. I did checkout the EF code from codeplex, and inherited my own class after understanding their source code. Here is the solution which I opted :-

public class MigrateDbToLatestInitializerConnString<TContext, TMigrationsConfiguration> : IDatabaseInitializer<TContext>
        where TContext : DbContext
        where TMigrationsConfiguration : DbMigrationsConfiguration<TContext>, new()
    {
        private readonly DbMigrationsConfiguration config;

        /// <summary>
        ///     Initializes a new instance of the MigrateDatabaseToLatestVersion class.
        /// </summary>
        public MigrateDbToLatestInitializerConnString()
        {
            config = new TMigrationsConfiguration();
        }

        /// <summary>
        ///     Initializes a new instance of the MigrateDatabaseToLatestVersion class that will
        ///     use a specific connection string from the configuration file to connect to
        ///     the database to perform the migration.
        /// </summary>
        /// <param name="connectionString"> connection string to use for migration. </param>
        public MigrateDbToLatestInitializerConnString(string connectionString)
        {
            config = new TMigrationsConfiguration
                          {
                              TargetDatabase = new DbConnectionInfo(connectionString, "System.Data.SqlClient")
                          };
        }

        public void InitializeDatabase(TContext context)
        {
            if (context == null)
            {
                throw new ArgumentException("Context passed to InitializeDatabase can not be null");
            }

            var migrator = new DbMigrator(config);

            migrator.Update();
        }
    }

 public static class DatabaseHelper
    {
        /// <summary>
        /// This method will create data base for given parameters supplied by caller.
        /// </summary>
        /// <param name="serverName">Name of the server where database has to be created</param>
        /// <param name="databaseName">Name of database</param>
        /// <param name="userName">SQL user name</param>
        /// <param name="password">SQL password</param>
        /// <returns>void</returns>
        public static bool CreateDb(string serverName, string databaseName, string userName, string password)
        {
            bool integratedSecurity = !(!string.IsNullOrEmpty(userName) || !string.IsNullOrEmpty(password));

            var builder = new System.Data.SqlClient.SqlConnectionStringBuilder
                {
                    DataSource = serverName,
                    UserID = userName,
                    Password = password,
                    InitialCatalog = databaseName,
                    IntegratedSecurity = integratedSecurity,
                };


            var db = new SrcDbContext(builder.ConnectionString);

            var dbInitializer = new MigrateDbToLatestInitializerConnString<SrcDbContext, SRC.DomainModel.ORMapping.Migrations.Configuration>(builder.ConnectionString);

            //following uses strategy to "CreateIfNotExist<>"
            dbInitializer.InitializeDatabase(db);

            return true;

        }
    }

Solution 2

You can make the MigrateDatabaseToLatestVersion initializer to use the connection string that was used by the context that triggered the migration in the first place. This is done by passing useSuppliedContext: true to the MigrateDatabaseToLatestVersion constructor as described in the docs. In your case:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<SrcDbContext, SRC.DomainModel.ORMapping.Migrations.Configuration>(useSuppliedContext: true));

Solution 3

What context is this running under? Website or Desktop App?

Under website, doing that is not a good idea. The database initializing strategy set against the type of context. So different connection strings with same type of context will override each other's init strategy.

If Desktop App, maybe include an extra utility to switch between database?

Either way I don't recommend doing this, but if you really want to do what you mentioned, it looks like you have to hack it.

    using (var context = new DbContext("<Your connection string right in here>"))
    {
        var constructors = typeof (DbMigrator).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
        var hackedDbMigrator = constructors[0].Invoke(new object[] { new Configuration(), context }) as DbMigrator;
        hackedDbMigrator.Update();
    }
Share:
16,487
Anand
Author by

Anand

Application Software developer based in Sydney Australia.

Updated on June 05, 2022

Comments

  • Anand
    Anand almost 2 years

    While trying to implement EF Migrations in my project I am stuck at one place.

    EF Code First MigrateDatabaseToLatestVersion accepts connection string Name from config.

    In my case database name get known at Runtime (User selects it from dropdown). Just the way DbContext either accepts, ConnectionString or connectionString Name in it's constructor, "MigrateDatabaseToLatestVersion" does not accept the same

    System.Data.Entity.Database.SetInitializer
    (new MigrateDatabaseToLatestVersion<SrcDbContext, SRC.DomainModel.ORMapping.Migrations.Configuration>(connString));
    

    Is there any other way to achieve this?

    • phil soady
      phil soady over 11 years
      you can pass Dynamic ConnectionStringName into Context during instantiation. You pass the context to to initializer. Why the need to pass here ? SrcDBConText should be created with Connection your like. Database.SetInitializer(new MigrateDatabaseToLatestVersion<YourContext, DemoMigrationConfiguration>()); should be enough
    • Anand
      Anand over 11 years
      -Thanks. I tried this also. It creates db with name {0} which is in my Config file. I want to use MigrateDatabaseToLatestVersion and not plain old initializer, where I will have to maintain two copies of initialization (one for migration, another for fresh DB creation). I am creating Unique constraints in migration.
    • phil soady
      phil soady over 11 years
      DbContext can be passed a connection string or Connection Name. I do the exact same thing your planning to do. 1 context used again multiple DBs for Migration. Then many Bounded contexts with initializer = null access the DBs. Not sure why it doesnt work for you sorry.
    • phil soady
      phil soady about 11 years
      I Was able to reproduce the problem now. When using connection string not in Web.config The root cause seems more obvious when trying MigrateDatabaseToLatestVersion<TContext, TMigrationConfiguration>(Database.Connection.ConnectionStrin‌​g)
  • Brian
    Brian over 8 years
    Thanks ... nice piece of sample code. Solved many of my problems.
  • Ergwun
    Ergwun over 6 years
    Thanks! How is this not the default behaviour??!??