How does Database.SetInitializer actually work? (EF code-first create database and apply migrations using several connection strings)

前端 未结 2 558
情话喂你
情话喂你 2021-01-03 04:48

I am trying to write a method to create a database and run migrations on it, given the connection string.

I need the multiple connections because I record an audit

相关标签:
2条回答
  • 2021-01-03 05:33

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

    The easiest way to provide connection - and be sure it works that way...

    1) Use your 'DbContext' class name - and define a connection in the app.config (or web.config). That's easiest, you should have a connection there that matches your context class name,

    2) If you put it into the DbContext via constructor - then be consistent and use that one. I'd also suggest to 'read' from config connections - and again name it 'the same' as your context class (use the connection 'name', not the actual string),

    3) if none is present - EF/CF makes the 'default' one - based on your provider - and your context's class name - which usually isn't what you want,

    You shouldn't customize with initializers for that reason - initializers should be agnostic and serve other purpose - setup connection in the .config - or directly on your DbContext

    Also check this Entity Framework Code First - How do I tell my app to NOW use the production database once development is complete instead of creating a local db?

    Always check 'where your data' goes - before doing anything.

    For how the initializer actually works - check this other post of mine, I made a thorough example

    How to create initializer to create and migrate mysql database?

    Notes: (from the comments)

    Connection shouldn't be very dynamic - config is the right place for it to be, unless you have a good reason.
    Constructor should work fine too.
    CreateDbIfNotExists doesn't work well together with the 'migration' initializer. You can just use the MigrateDatabaseToLatestVersion initializer. Don't 'mix' it

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

    To point to connection - just use its 'name' and put that into constructor.

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

    Regarding entertaining 'multiple databases' with migrations (local and remote from one app) - not exactly related - but this link - Migration not working as I wish... Asp.net EntityFramework

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

    It is getting interesting here. I did manage to reproduce the problems you're facing actually. Here is a short breakdown on what I think it's happening:

    First, this 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 custom initializer from my other post, which controls migration/creation 'manually')

    That worked fine w/o an Initializer. Once I switched that on, I ran into some curious problems.

    I deleted Db-s (two, for each connection). I expected to either not work, or create one db, then another in the next pass (like it did, w/o migrations, just 'Create' initializer).

    What happened, to my surprise - is it actually created both databases on the first pass ??

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

    It created first instance on my call within the flip. Then on the first access to 'model', it invoked the initializer. Migrator took over (having had no db-s). During the migrator.Update(); it actually constructs the MyContext (I'm guessing via generic param in Configuration) - and calls the 'default' empty ctor. That had the 'other connection/name' by default - and creates the other Db all as well.

    So, I think this explains what you're experiencing. And why you had to create the 'Factory' to support the Context creation. That seems to be the only way. And setting some 'AppDomain' wide 'connection string' (which you did well actually) which isn't 'overriden' by default ctor call.

    Solution that I see is - you just need to run everything through factory - and 'flip' connections in there (no need for static connection, as long as your factory is a singleton.

    0 讨论(0)
  • 2021-01-03 05:40

    You can supply a configuration in the MigrateDatabaseToLatestVersion constructor. If you set the initializer in the DbContext you can also pass a 'true' to use the current connection string.

    0 讨论(0)
提交回复
热议问题