EF Code First MigrateDatabaseToLatestVersion accep

2020-05-25 18:08发布

问题:

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?

回答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;

        }
    }


回答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));


回答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();
    }


回答4:

The is a issue with Migrations call the DbContext dervied class with parameter. Once this is solved it should work. see here for a sample solution. EntityFramework code-first custom connection string and migrations