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);
}
(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)
static string _connection;
public MyContext()
: base(_connection ?? "DefaultConection")
{
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyNamespace.Migrations.Configuration>());
}
public MyContext(string connection)
: base(connection)
{
_connection = connection;
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyNamespace.Migrations.Configuration>());
}
Few points:
1) Define static
connection in your context - and as you set a 'new one' change it to hold the latest value
. That helps keeping things in synchronization - no matter who accesses the DbContext has the same one (it's similar in concept to Factory
just easier on the eyes,
2) Problem with Migrations
is - that MigrateDatabaseToLatestVersion
caches the connection (and entire configuration internally) in a readonly
field internally - and even if you change it in your DbContext
, or whatever you set outside, it gets 'out of sync`.
There is no way getting around that but to make a 'new initializer'.
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'.
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...
for (var flip = true; true; flip = !flip)
{
using (var db = new MyContext(flip ? "Name=DefaultConnection" : "Name=OtherConnection"))
{
// usual db code
}
}
The key is to
set initializer each time you set the connection
.
The old one is still around - and the old connection (you can't delete the Db while app is running)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With