I am trying to write an easy to understand DBContext
class that takes a custom connection string, can run migrations, and that I allows me to generate migrations using Package Manager.
I seem to be going around in circles.
I have been able to get it working using code that feels awful to me. I documented this in my answer to This question on connection string and migrations.
Radek's answer looks much better than mine, but I find that when I implement it and then try and create a migration in Package Manager I get the message
The target context 'DataLayer.Context' is not constructible. Add a default constructor or provide an implementation of IDbContextFactory.
Where DataLayer.Context
is my context class.
I don't want to provide an implementation of IDbContextFactory
( and Radek's answer seems to indicate it isn't needed )
UPDATE:
I can generate a migration if I include a constructor with no parameter. For example
public Context() : base("ConnectionStringName") { }
For my context creation I pass the name of the connection string in app.config
public Context(string connString) : base(connString)
{
Database.SetInitializer(new CustomInitializer());
Database.Initialize(true);
}
At last I am able to both generate migrations, and run migrations for databases that the user selects.
HOWEVER:
When I drop the database and then run my app I have problems.
The initialiser code I am using, from the link above is
public class CustomInitializer : IDatabaseInitializer<Context>
{
public void InitializeDatabase(Context context)
{
try
{
if (!context.Database.Exists())
{
context.Database.Create();
}
else
{
if (!context.Database.CompatibleWithModel(false))
{
var configuration = new Configuration();
var migrator = new DbMigrator(configuration);
migrator.Configuration.TargetDatabase =
new DbConnectionInfo(context.Database.Connection.ConnectionString);
IEnumerable<string> migrations = migrator.GetPendingMigrations();
foreach (string migration in migrations)
{
var scriptor = new MigratorScriptingDecorator(migrator);
string script = scriptor.ScriptUpdate(null, migration);
context.Database.ExecuteSqlCommand(script);
}
}
}
}
catch (Exception ex)
{
}
}
}
When I drop the database a new one gets created but it has no tables. That would be because my table creation code is all in my first migration.
So the code inside the !context.Database.CompatibleWithModel(false)
condition does not run.
However alas, the code also does not run the second time around when it should have the metadatamodel.
Now to try and get the migration running…
SADNESS: No with Radek's custom initializer so far.
From NSGaga's comments I have resorted to exiting the application if I change connection.
var master = new myMDIForm();
master.ConnectionType = connectionType; // being an enum of the different connection names in app.config
while (master.ConnectionType != ConnectionType.None )
{
Application.Run(master);
}
Best Answer
(note: I have no idea about your custom initializer, I just saw that - this is a general solution to the
connection caching
problem and migration initializer - but should work with variations)This is what works for me - and based on the buggy behavior that I described here.
Few points:
1) Define
static
connection in your context - and as you set a 'new one' change it to hold thelatest value
. That helps keeping things in synchronization - no matter who accesses the DbContext has the same one (it's similar in concept toFactory
just easier on the eyes,2) Problem with
Migrations
is - thatMigrateDatabaseToLatestVersion
caches the connection (and entire configuration internally) in areadonly
field internally - and even if you change it in yourDbContext
, or whatever you set outside, it gets 'out of sync`.3) What @Radek discovered was actually the solution for that problem - and in line with (2). I just removed the
Initialize(true)
as it's unnecessary - that gets called when 'the time is right'.That's about it.
With this I can now
flip
my connections around the clock - and as I want.(meaning I can change connection at
runtime
- migrate/create two, or more, databases and keep changing connections and working on them simultaneously)And this is the code I actually use to cycle between connections...