How does Database.SetInitializer work? (EF code database creation and migration using multiple connection strings)

I am trying to write a method to create a database and start migration to it, given the connection string.

I need several connections because I write the audit log to a separate database. I get connection strings from app.config using code like

ConfigurationManager.ConnectionStrings["Master"].ConnectionString; 

The code works with the first connection string defined in my app.config, but not with the others, which makes me think that somehow it gets the connection string from app.config somehow, I don’t know.

My code to create a database, if it does not exist,

 private static Context MyCreateContext(string ConnectionString) { // put the connection string where the factory method can get it AppDomain.CurrentDomain.SetData("ConnectionString", ConnectionString ); var factory = new ContextFactory(); // I know I need this line - but I cant see how what follows actually uses it Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context,DataLayer.Migrations.Configuration>()); var context = factory.Create(); context.Database.CreateIfNotExists(); return context } 

The code in Migrations.Configuration is

 Public sealed class Configuration : DbMigrationsConfiguration<DataLayer.Context> { public Configuration() { AutomaticMigrationsEnabled = false; } } 

Factory Context Code -

  public class ContextFactory : IDbContextFactory<Context> { public Context Create() { var s = (string)AppDomain.CurrentDomain.GetData("ConnectionString"); return new Context(s); } } 

Thus, I set the connection string before creating the context.
Where can I make a mistake, given that the connection strings are all the same except for the database name, and the migration code works with one connection string, but does not work with others?

I wonder how my problem is with understanding how Database.SetInitializer works. I guess something about reflection or generics. How do I call SetInitializer, link the link to my actual context?

I tried the following code, but the migrations are not performed

  private static Context MyCreateContext(string ConnectionString) { Database.SetInitializer(new MigrateDatabaseToLatestVersion<Context, DataLayer.Migrations.Configuration>()); var context = new Context(ConnectionString); context.Database.CreateIfNotExists(); } 

This question seems to be related

UPDATE:

I can make migrations work if I reference the connection string using public MyContext (): base ("MyContextConnection") - which indicates the configuration

I also managed to get migrations to work using different instances of the context if I created the ContextFactory class and passed the connection to it, referencing the global one. (See My answer to a related question).

Now I wonder why this is so complicated.

+3
entity-framework-5 code-first-migrations
Apr 05 '13 at 9:13
source share
2 answers

I'm not sure exactly what problems you are facing, but let me try

The easiest way to connect is to make sure it works that way ...

1) Use your 'DbContext' class name - and define the connection in app.config (or web.config). This is easiest, you should have a connection matching your context class name,

2) If you put it in DbContext through the constructor - then be consistent and use it. I also suggest “reading” from configuration connections and again calling it “the same” as your context class (use the name “name” rather than the actual string),

3) if none are present - EF / CF does the "default" - based on your provider - and your context class name - which is usually not what you want,

You should not configure using initializers for this reason - initializers should be agnostic and serve other purposes - configure the connection in .config - or directly on DbContext

Also check out this Entity Framework Code First - How do I tell my application that NOW uses the production database after development is complete instead of creating a local db?

Always check where your data goes - before doing anything.

For how the initializer really works , check out this other post, I made a detailed example

How to create an initializer to create and migrate a mysql database?

Notes: (from comments)

The connection does not have to be very dynamic - config is the right place for it unless you have a good reason.
The constructor also works great.
CreateDbIfNotExists does not work with the migration initializer. You can simply use the MigrateDatabaseToLatestVersion initializer. Don't mix it

Or - put something like public MyContext() : base("MyContextConnection") - which points to <connectionStrings> in config

To indicate a connection, simply use its "name" and put it in the constructor.

Or use somehting as ConfigurationManager.ConnectionStrings["CommentsContext"].ConnectionString

Concerning entertaining "multiple databases" with migrations (local and remote from one application) - not quite connected - but this link - Migration does not work as I wish ... Asp.net EntityFramework

Update: (further discussion here - Is adding a class that inherits from something a violation of solid principles if it changes the behavior of the code? )

It is interesting here. I was able to reproduce the problems you are facing. Here is a brief breakdown of what I think is happening:

Firstly, it worked “happily”:

 Database.SetInitializer(new CreateAndMigrateDatabaseInitializer<MyContext, MyProject.Migrations.Configuration>()); for (var flip = false; true; flip = !flip) { using (var db = new MyContext(flip ? "Name=MyContext" : "Name=OtherContext")) { // insert some records... db.SaveChanges(); } } 

(I used a custom initializer from my other post, which controls the migration / creation "manually")

This worked great without an Initializer. As soon as I turned it on, I ran into some interesting problems.

I deleted Db-s (two, for each connection). I expected that either it would not work or it would create one db and then another in the next pass (for example, without migration, just the “Create” initializer).

What happened, to my surprise, did it actually create both databases on the first pass ??

Then, being a curious person :), I set breakpoints on MyContext ctor and debugged through migrator / initializer. Again empty / no db-s, etc.

He created the first instance of my call in flip . Then, at the first access to the "model", he called the initializer. The migrator occupied (did not have dB). During migrator.Update(); it actually creates MyContext (I guess through the general parameter in Configuration) - and calls the "default" empty ctor. This had a "different connection / name" by default, as well as all other Db.

So, I think it explains what you are experiencing. And why did you need to create a "Factory" to support the creation of Context. This seems to be the only way. And by setting some "AppDomain" connection string (which you really actually did), which by default is not canceled by the ctor call.

The solution I see - you just need to run everything through the factory - and have the 'flip' connections there (no need for a static connection if your factory is single.

+5
Apr 6 '13 at 0:46
source share

You can specify the configuration in the MigrateDatabaseToLatestVersion constructor. If you set the initializer to DbContext, you can also pass "true" to use the current connection string.

0
Mar 01 '16 at 11:02
source share



All Articles