Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

Firing an Entity Framework Database Initializer from within DbContext


:P
On this page:

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:

Posted in Entity Framework  

The Voices of Reason


 

Knaģis
March 26, 2013

# re: Firing an Entity Framework Database Initializer from within DbContext

1. doesn't the lock prevent from calling the utility method for different TContext types?
2. why don't you simply call the SetInitializer in the static constructor for the DbContext?

Rick Strahl
March 26, 2013

# re: Firing an Entity Framework Database Initializer from within DbContext

Knaģis - good points!

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.

Julie Lerman
March 26, 2013

# re: Firing an Entity Framework Database Initializer from within DbContext

Hey Rick,
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??

Rick Strahl
March 26, 2013

# re: Firing an Entity Framework Database Initializer from within DbContext

@Julie - Thanks for the pointer. It be nice if that works, does it actually work? A static constructor in a base class isn't fired unless a property of the base class is accessed if I understand this right (and based on this: http://stackoverflow.com/questions/4652454/whats-the-best-way-to-ensure-a-base-classs-static-constructor-is-called) - I wouldn't expect that static base class constructor to fire consistently.

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.

A static constructor would be fine for a single context, but if you have many contexts and you have many 

Thanks everyone for making me re-evaluate what I know about static constructors - learned something new there. I was actually under the impression that static constructors ONLY fire if static properties are accessed. Turns out they fire if any properties on the instance it's declared on are accessed. Live and learn.

Sam Wheat
October 14, 2013

# re: Firing an Entity Framework Database Initializer from within DbContext

>>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.

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.

Fahmy
December 13, 2016

# re: Firing an Entity Framework Database Initializer from within DbContext

Is this valid in a webFarm enviroment (Multiple servers) ?


Derek Licciardi
April 17, 2017

# 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.


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2020