Firing an Entity Framework Database Initializer from within DbContext
One thing about Entity Framework that bugs me are how database initializers work. Database Initializers are used to initialize the database and determine the behavior of how the Entity Framework model interacts with the database. The default EF behavior compares the model to any meta data in the database and if it's not valid, throws a failed meta data exception. Initializers serve a valid purpose, but I tend to forget exactly what object I have to call to initialize them and what class I have to override to create them. Worst of all though, because database initializers are supposed to run only once per AppDomain they have to be placed in a very specific location in a project to fire at application startup. In other words, it's fairly hard to internalize database initializers.
How Database Initializers work
Database initializers initialize the database and are intended to run exactly once per AppDomain. This means they need to be run on application startup - either in a desktop apps startup code or in Application_Start in Web application.
For example to use an initializer in ASP.NET, you use syntax like the following:
protected void Application_Start() { Database.SetInitializer<ClassifiedsContext>(new DropCreateDatabaseIfModelChanges()); }
It's unlikely that you'll use one of the default database initializers in a live application, since they are pretty destructive but they can be useful for testing or during development when the database changes a lot. The more common scenario like is to use the Migration initializer:
Database.SetInitializer<ClassifiedsContext>( new MigrateDatabaseToLatestVersion<ClassifiedsContext,MigrationConfiguration>());
Uff, that's a mouthful, eh? I tend to subclass this mouthful with my own class to make the initializer a little more approachable.
public class ClassifiedsModelMigrateInitializer : MigrateDatabaseToLatestVersion<ClassifiedsContext,ClassifiedsBusiness.Migrations.MigrationConfiguration> { }
Initializers can override the Initialize() and Seed methods(). The Initialize method is responsible for checking the context and perform any additional initialization you might have to do. The default implementation checks the metadata in the database (if it exists) against the model. If you don't want that to happen you can simply implement an empty initializer like so:
public class QueueMessageManagerContextInitializer : IDatabaseInitializer<QueueMessageManagerContext> { protected void Seed(QueueMessageManagerContext context) { } public void InitializeDatabase(QueueMessageManagerContext context) { // do nothing Seed(context); } }
which can then be used like this:
Database.SetInitializer<QueueMessageManagerContext>(new QueueMessageManagerContextInitializer());
Turns out there's actually an even simpler way to have a non-actionable database Initializer - simply pass null:
Database.SetInitializer<QueueMessageManagerContext>(null);
My Problem
Earlier today I ran into just this problem. I have a single database with two DbContexts connected to it. The first is a very rich model that uses migrations and so uses the Db's meta data for validation and triggering migrations as needed. The second is a small component with a tiny model of two tables that is intended to just work without checking the existing metadata in the database. The second context tests out fine when running in its own database with hand created tables to map to, but if dumping those same tables to the live database that is also accessed by the first context it fails.
The above 'empty' initialization strategies work well to allow me to bypass the model meta data validation on startup.
But there's another problem here: The second context is part of a small reusable component that's meant to be generic. Requiring a custom database initializer is a pain for this because the initializer forces the consumer of my component to externally create the initializer and call it. IOW, the consumer has to remember to set up the initializer and place it in the right place in the startup sequence.
That sucks!
Internalizing the Database Initializer
Incidentally this is something that's bugged me a quite a bit about EF in other places. I always forget exactly what I need for implementing a database initializer in an app. How does it relate to the project what do I need to instantiate and where's the best place to put it etc. etc. It's ugly and not very discoverable. Frankly the only way I remember is to open up another project and see how I did it previous :-)
It seems to me that the initializer invocation is not an application responsibility but the responsibility of the context and that's where I would expect that behavior to live.
So I got to thinking - wouldn't it be nice to actually make the initialization more generic so that it can be called from anywhere and still be guaranteed to always fire just once.
Here's a DbContext utility method that does just that:
public static class DbContextUtils<TContext> where TContext : DbContext { static object _InitializeLock = new object(); static bool _InitializeLoaded = false; /// <summary> /// Method to allow running a DatabaseInitializer exactly once /// </summary> /// <param name="initializer">A Database Initializer to run</param> public static void SetInitializer(IDatabaseInitializer<TContext> initializer = null) { if (_InitializeLoaded) return; // watch race condition lock (_InitializeLock) { // are we sure? if (_InitializeLoaded) return; _InitializeLoaded = true; // force Initializer to load only once System.Data.Entity.Database.SetInitializer<TContext>(initializer); } } }
Nothing fancy here - all this code really does is check to see if this code was previously by using a static flag to hold the state. If you recall, static properties/fields are global to the AppDomain so this SetInitializer call fires exactly once per AppDomain. This code needs to be called before the first full context invocation.
If the goal is to internalize this code as part of the context, it's easy to stick it into the constructor of the DbContext subclass you create. Here's an example:
public class QueueMessageManagerContext : DbContext { public QueueMessageManagerContext() { // don't validate the schema DbContextExtensions<QueueMessageManagerContext>.SetInitializer(null); } public DbSet<QueueMessageItem> QueueMessageItems { get; set; } }
Now you don't have to worry about when the initializer is called because the first access to your context automatically initializes the context using the specified initializer. This also keeps all behavior relative to the Context in one place, so personally I like this. You can also still use app startup code to call the method directly just like calling SetInitializer directly.
This is a small thing of course, but it's something that's important to me as in my current app I'm working on with a client we have many small self contained components that have micro EF models. I can now easily force all of those components to non check the meta data when they start and all can share the same database easily.
Resources:
Other Posts you might also like
The Voices of Reason
# re: Firing an Entity Framework Database Initializer from within DbContext
1. Yes you're right - the method is unique to each type due to the generic type signature, but the flag and lock vars are not. So yes - that needs fixing. Making the class DbContextUtils<TContext> should do the trick... Fixed in code above.
2. I thought about a static constructor, but how do you get make that generic then? How do you get the static constructor fired and how do you get the initializer parameter into it? I couldn't figure out a way to make that work, unless you explicitly implement it on each context. I want something I can just call from anywhere and be assured it only fires once.
# re: Firing an Entity Framework Database Initializer from within DbContext
With some help from Arthur Vickers, I came up with a different pattern that has a basecontext with a static constructor where I'm setting the initializer. It's something like what you are talking about in your last comment but not quite the same. You can see the class and how I use it in the bottom of this article: http://msdn.microsoft.com/en-us/magazine/jj883952.aspx. Does that help??
# re: Firing an Entity Framework Database Initializer from within DbContext
As I mentioned to Knagis - something has to trigger that static constructor.
Unless I'm missing something - which is entirely possible :-) - I think using a static constructor is to fickle and logistically confusing when you're dealing with a generic operation especially since you need some object state (the inititializer) to make it work.
# re: Firing an Entity Framework Database Initializer from within DbContext
That statement seems both odd and ironic because initializing the db from the dbcontext appears to be the source of your angst.
My thought is that initializing and verifying the db should be done explicitly early in the app lifetime just like we've been doing for years and years long before EF came along. When its time to create an instance of your context you should know your db exists and that you are able to connect to it.
The dbcontext has enough work to do without worrying about if a db exists. If you are using your dbcontext per unit of work (as you should) then you want that thang to load as fast as possible and not do one iota of work it does not have too.
Also, there are certain circumstances where you may want your initialization code to run more than one time. For example, I am writing an inventory app that keeps a copy of the db locally as well as on the server. I need to check db versions when the user logs in and if/when they connect the server. I use two different instances of the same context for both databases, just a different connection string.
So, IMHO, the argument about using a static constructor is an answer to the wrong question. The better question is "Does db initialization logic belong in the dbcontext?" and I think the answer to that question is no.
# re: Firing an Entity Framework Database Initializer from within DbContext
Is this valid in a webFarm enviroment (Multiple servers) ?
# re: Firing an Entity Framework Database Initializer from within DbContext
There's an easy way to solve this specific problem and that is to use schema's in SQL Server. Place each Context into its own Schema and you will get multiple _MigrationHistory tables and multiple views of the model's metadata, each specific to the DbContext that you want to use. If your component doesn't use Code First or won't allow you to move off of the DBO schema, then move your application code from DBO to some other schema and you're all set.
# re: Firing an Entity Framework Database Initializer from within DbContext
2. why don't you simply call the SetInitializer in the static constructor for the DbContext?