When I create a context with a default connection string (as read from the app.config
) the database is created and the migrations work - basically everything is in order. Whereas when the connection string is created programatically (using SqlConnectionStringBuilder
):
A
);CreateDbIfNotExists()
creates the newest version of database model but the migration mechanisms are not invoked (scenario B
).In A
an exception is thrown when I wish to access the database, as - obviously - it isn't there. In B
database is created properly migration mechanisms are not called, as is the case in standard connection string.
app.config:
"Data Source=localhost\\SQLEXPRESS;Initial Catalog=Db13;User ID=xxx;Password=xxx
"
builder:
sqlBuilder.DataSource = x.DbHost;
sqlBuilder.InitialCatalog = x.DbName;
sqlBuilder.UserID = x.DbUser;
sqlBuilder.Password = x.DbPassword;
initializer:
Database.SetInitializer(
new MigrateDatabaseToLatestVersion<
MyContext,
Migrations.Configuration
>()
);
Specs: Entity Framework: 5.0, DB: SQL Server Express 2008
If your migration does not work correctly try to set Database.Initialize(true)
in DbContext ctor.
public CustomContext(DbConnection connection)
: base(connection, true)
{
Database.Initialize(true);
}
I have similar problem with migrations. And in my solution I have to always set database initializer in ctor, like below
public CustomContext(DbConnection connection)
: base(connection, true)
{
Database.SetInitializer(new CustomInitializer());
Database.Initialize(true);
}
In custom initializer you have to implement InitalizeDatabase(CustomContex context)
method, eg.
class CustomInitializer : IDatabaseInitializer<CustomContext>
{
public void InitializeDatabase(CustomContext context)
{
if (!context.Database.Exists || !context.Database.CompatibleWithModel(false))
{
var configuration = new Configuration();
var migrator = new DbMigrator(configuration);
migrator.Configuration.TargetDatabase = new DbConnectionInfo(context.Database.Connection.ConnectionString, "System.Data.SqlClient");
var migrations = migrator.GetPendingMigrations();
if (migrations.Any())
{
var scriptor = new MigratorScriptingDecorator(migrator);
string script = scriptor.ScriptUpdate(null, migrations.Last());
if (!String.IsNullOrEmpty(script))
{
context.Database.ExecuteSqlCommand(script);
}
}
}
}
}
UPDATED
He is a solution, with NO Connection strings in app.config. Uses automatic migrations and 2 databases using the same context. The real runtime supplied Connection. Approach.
APP.CONFIG (Uses EF 6)
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
</startup>
<entityFramework>
<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">
<parameters>
<parameter value="Data Source=localhost; Integrated Security=True; MultipleActiveResultSets=True" />
</parameters>
</defaultConnectionFactory>
</entityFramework>
</configuration>
I rewrote the code to make as small as possible for Demo:
using System;
using System.Data.Common;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.Migrations;
namespace Ef6Test {
public class Program {
public static void Main(string[] args) {
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Ef6Ctx, Ef6MigConf>());
WhichDb.DbName = "HACKDB1";
var sqlConn = GetSqlConn4DBName(WhichDb.DbName);
var context = new Ef6Ctx(sqlConn);
context.Database.Initialize(true);
AddJunk(context);
//sqlConn.Close(); //?? whatever other considerations, dispose of context etc...
Database.SetInitializer(new MigrateDatabaseToLatestVersion<Ef6Ctx, Ef6MigConf>()); // yes its default again reset this !!!!
WhichDb.DbName = "HACKDB2";
var sqlConn2 = GetSqlConn4DBName(WhichDb.DbName);
var context2 = new Ef6Ctx(sqlConn2);
context2.Database.Initialize(true);
AddJunk(context2);
}
public static class WhichDb { // used during migration to know which connection to build
public static string DbName { get; set; }
}
private static void AddJunk(DbContext context) {
var poco = new pocotest();
poco.f1 = DateTime.Now.ToString();
// poco.f2 = "Did somebody step on a duck?"; //comment in for second run
context.Set<pocotest>().Add(poco);
context.SaveChanges();
}
public static DbConnection GetSqlConn4DBName(string dbName) {
var sqlConnFact =
new SqlConnectionFactory(
"Data Source=localhost; Integrated Security=True; MultipleActiveResultSets=True");
var sqlConn = sqlConnFact.CreateConnection(dbName);
return sqlConn;
}
}
public class MigrationsContextFactory : IDbContextFactory<Ef6Ctx> {
public Ef6Ctx Create() {
var sqlConn = Program.GetSqlConn4DBName(Program.WhichDb.DbName); // NASTY but it works
return new Ef6Ctx(sqlConn);
}
}
public class Ef6MigConf : DbMigrationsConfiguration<Ef6Ctx> {
public Ef6MigConf() {
AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;
}
}
public class pocotest {
public int Id { get; set; }
public string f1 { get; set; }
// public string f2 { get; set; } // comment in for second run
}
public class Ef6Ctx : DbContext {
public DbSet<pocotest> poco1s { get; set; }
public Ef6Ctx(DbConnection dbConn) : base(dbConn, true) { }
}
}