EF Code First DB Initialization Using Web.Config

The DBContext used by Entity Framework Code First allows you to set a strategy for how your database is updated when your application starts.  While this is a huge time saver for development it is usually not something you want to allow for production.  The last thing we need is to deploy an update to production and have the application drop and recreate the production database.

The following database initialization options are available:

  • DropCreateDatabaseIfModelChanges – This strategy recreates your database only if the Entity Framework model is different from the database or the database does not exist. This is the most common strategy used while developing
  • CreateDatabaseIfNotExists (Default) - If no database is found it will be created according to your Entity Framework Model.
  • DropCreateDatabaseAlways - This strategy always drops the database and recreates it. This is helpful for integration and database test projects where a fresh database with deterministic data is created for each test.
  • Custom – Used to Implement your own strategies or add seed and test data to an existing strategy. This post shows an example of extending the strategy DropCreateDatabaseIfModelChanges and seeding the new database with test data
  • null – Disables the EF Code First Initialization process

Given the following model and context:

public class Wizard
{
	public virtual int WizardId { get; set; }
	public virtual string Name { get; set; }
	public virtual int Level { get; set; }
	public virtual ICollection<spell> Spells { get; set; }
}

public class Spell
{
	public virtual int SpellId { get; set; }
	public virtual string Name { get; set; }
	public virtual int LevelRequired { get; set; }
}

public class FantasyContext : DbContext
{
	public DbSet<wizard> Wizards { get; set; }
	public DbSet<spell> Spells { get; set; }
}

We can create a Database Initializer strategy that will drop and recreate the database seeding it with test data whenever the model changes.

public class FantasyContextInitializer
	: DropCreateDatabaseIfModelChanges<FantasyContext>
{
	protected override void Seed(FantasyContext context)
	{
		var Gandalf = new Wizard
		{
			Name = "Gandalf the White",
			Level = 100,
			Spells = new[]
			{
				new Spell { Name = "Illumination", LevelRequired = 10 },
				new Spell { Name = "Sword of Power", LevelRequired = 50 },
				new Spell { Name = "Lightning Strike", LevelRequired = 60 }
			}
		};
		var Merlin = new Wizard
		{
			Name = "Merlin",
			Level = 100,
			Spells = new[]
			{
				new Spell { Name = "Magical Kinesis", LevelRequired = 10 },
				new Spell { Name = "Blinding Light", LevelRequired = 50 },
				new Spell { Name = "Destructive Blast", LevelRequired = 60 }
			}
		};
		context.Wizards.Add(Gandalf);
		context.Wizards.Add(Merlin);
		base.Seed(context);
	}
}

The canonical example for adding this strategy to the DbContext is by adding a single line to the Global.asax Application_Start method.

protected void Application_Start()
{
	Database.SetInitializer(new FantasyContextInitializer());
	// Other startup stuff goes here
}

However, this makes it difficult to remove the initializer for release builds or to define a different strategy for Integration Tests. Fortunately, you can define the DbContext Initializers in the config file and change or remove them using XLST transformations (For an excellent primer on Web and App config file transformations see Oleg Sych’s article Here).

To use the web.config you must first remove the SetInitializer line from the Application_Start method. Then add the following section in your web.config file.

<appsettings>
	<add key="DatabaseInitializerForType Wizards.Models.FantasyContext, Wizards"
	         value="Wizards.Models.FantasyContextInitializer, Wizards" />
	<!-- Other stuff here -->
</appsettings>

The key starts with DatabaseInitializerForType and includes the fully qualified assembly name for your Context object. The value is the fully qualified assembly name for the initialization strategy object, FantasyContextInitializer in the example. The next step is to add a transformation to the

image Web.Release.config file to remove the initializer when we publish a release build. If you do not have a Web.Release.config file in your project then right click on the web.config file and choose Add Config Transformations. This will add a configuration file transformation for each build type in your solution.  For example, the default debug and release builds will result in a Web.Debug.config and Web.Release.config files added to your solution.


Adding  the following lines to your Release config file will remove the Database Initializer from the web.config

<appsettings>
    <add xdt:transform="Remove"
         xdt:locator="XPath(//add[starts-with(@key, 'DatabaseInitializerForType')])" />
</appsettings>

This transformation states: Within the appsettings section locate an add element that has a key attribute that starts with DatabaseInitializerForType and remove it.

 

Summary

XML Transformations can be a bit tricky at first to setup but, once mastered, can be a powerful tool for setting up your different environments.

 

.

, ,

6 Comments

  • Jeff Odell says:

    Thanks! Implemented in my latest project.

    A clarification – in the config file:

    “Wizards” refers to the name of the assembly containing the DbContext and/or the initializer class.

    Oleg’s article is excellent. The MSDN docs for transformation are here:

    http://msdn.microsoft.com/en-us/library/dd465326.aspx

  • Pankaj says:

    Thanks ! Very neat post and a great one for someone working with EF Code-first.

    In your explanation for web.config settings, you can add the point given by @Jeff Odell about “Wizards”.

    Great Work !!

  • Stellan D says:

    Interesting post indeed!

    One question though;
    How would you implement the relation if both wizards knew the same spell?

    I.e. if both Merlin and Gandalf knew the spell “Illumination”?

  • Hi just wanted to give you a quick heads up and let you know a few of the pictures aren’t loading correctly. I’m not sure why but I think its a linking issue. I’ve tried it in two different browsers and both show the same results.

  • Randy Patterson says:

    Can you be more specific? I removed my browser cache and refreshed….all the pictures were available.

  • Hey There. I found your blog using msn. This is a very well written article. Ill make sure to bookmark it and return to read more of About . Thanks for the post. I will definitely return.