Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF multi-context with a plugin-style system. How to apply migrations at runtime?

I have a web application which is supposed to be composed as a series of plugins into a core infrastructure. A plugin is a compiled CLR dll + some content files which will be put in a certain location. I'm using Autofac to scan and register types out of the assembly, and some fancy routing to serve controllers and assets from there. But, since each plugin assembly can contain a DbContext (by convention each will use its own database), I can't quite figure out what to do there.

Now I've found a lot of stuff around how to use multiple contexts but it all requires knowing what these will be at development time. My application does not know what contexts will be used until runtime.

What I'm looking for ideally is would like is some way to do

ApplyMigrations<MyDbContext, MyDbConfiguration>();

Though I would also somehow have to provide an ordered set of migrations to apply (if using explicit migrations).

Where I'm stumbling currently is the standard

Database.SetInitializer(...)

since it is a static singleton and each dbcontext in my system has its own initializer.

like image 782
George Mauer Avatar asked May 22 '15 13:05

George Mauer


2 Answers

First, of all SetInitializer stores the IDatabaseInitializer objects in a dictionary with the context Type as key, so theoretically multiple calls of SetInitializer should work fine.

On the other hand, if that doesn't work, another option is to explicitly perform initialization:

class YourContext : DbContext
{
    static YourContext()
    {
        Database.SetInitializer<YourContext>(YourMigratingDatabaseInitializer);
        using (var context = new YourContext())
        {
            context.Database.Initialize(false);
        }
    }

    public YourContext()
    {
        Database.SetInitializer<YourContext>(null);
    }
}
like image 147
jjj Avatar answered Nov 11 '22 21:11

jjj


Perhaps write an interface for bootstrapping a plugin, so IPluginBootstrapper - from here you could pass in a ContainerBuilder to add to a collection of services that the plugin provides, or return a built Container that the plugin builds and combine it on the host. In this way, you're pushing the responsibility of the all the DB seed/migration work into each plugin - so when you drop in a new dll, when it's bootstrapped it can run it's own upgrade path.

Another alternative, perhaps you can have a configuration section which defines a pair of types, so Tuple and tell Autofac to find all pairs of these in the plugins directory, and then call SetInitializer with what is resolved?

like image 20
leon.io Avatar answered Nov 11 '22 22:11

leon.io