Saturday, September 8, 2012

Entity Framework Code First Web.config Initialization

Even though as a blogger you often blog code, blogs aren't working code, obviously. A blog post about a particular technology will slowly fade in relevance as time passes and the world moves on. But at the same time if a better way of doing something that you recently wrote about comes along, you'd want to update the post in question, so that's what I'm doing here about EF Code First Migrations, now that we're up to EF 5.0.

In Entity Framework Migrations strategies: the Semi-Automatic I talked about the DbMigrator class to run your migration scripts, invoked from the global.asax on Application_Start() for instance, but that was then: this is now. Or as they say in Perth "Yeah ... nah!". The way you specify the initializer for your DbContext has changed a lot in recent times, exhibiting all the stability of a game of jenga. But since the Jan 12 EF 4.3 Configuration File Settings post from the ADO.Net team, we can all consider ourselves schooled in the state-of-the-art in EF Code First initialization options, and should therefore move the whole show to the nearest config file.

Flickr photo
FlickrTransformers-Movie-Screen-Shots-04, by jalopnik. Web.config Transformer Man

Since EF 4.3 there has been an entityFramework section in the Web.config and as the EF team point out in that blog post you can use this to set a migration strategy. Much better than doing it through the code. Why? Because obviously you may not have the same strategy in your local dev, testing, and production environments. "...if you are using Code First Migrations, you can configure the database to be migrated automatically using the MigrateDatabaseToLatestVersion initializer." Did you get that? There's a new addition to the original 3 initializers included with EF:
  1. CreateDatabaseIfNotExists (default)
  2. DropCreateDatabaseWhenModelChanges
  3. DropCreateDatabaseAlways
  4. MigrateDatabaseToLatestVersion <- now that's what I'm talking about!
You can completely disable initialization too, by simply changing the disableDatabaseInitialization value to "true". You may be nervous about the unintended side-effects of EF Code First on your production database, so it's nice to have an off switch like this. Let's be perfectly honest here - running any sort of initialization option against a live database is not for the faint-hearted.

To get cracking with this new initializer, just associate it with your context in your app's Web.config:
<configuration>
  ...
  <entityFramework>
    <contexts>
      <context type="MyAssembly.MyContext, MyAssembly" disableDatabaseInitialization="false" >
        <databaseInitializer type="System.Data.Entity.MigrateDatabaseToLatestVersion`2[[MyContext, MyAssembly], [MyConfiguration, MyAssembly]], EntityFramework" />
        </context>
      </contexts>
   </entityFramework>
<configuration>
That databaseInitializer type attribute value is just confusing: at least I found myself confused the first time I had to whip one of these up in the web.config, so it might help to think of it as being the same as:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<MyContext, MyConfig>());
The MigrateDatabaseToLatestVersion generic initializer takes two type parameters - "When we create an instance of this initializer we need to specify the context type and the migrations configuration - the migrations configuration is the class that got added to our Migrations folder when we enabled Migrations.", from the Code First Migrations article in the MSDN Data Developer Center - so that's why there's the `2 in the config.

Remember that this new web.config option still requires that you explicitly run through the 'add-migration...' and 'update-database' steps I outlined in my last EF post. The purpose of this post is just to concentrate on moving the old code-based DbMigrator functionality into the web.config, since that sort of thing - your choice of initialization strategy - is perfectly suited to an environmentally configurable setting, much like a service endpoint or email address.

Flickr photo
FlickrTransformers DOTM Deluxe Bumblebee 05, by Gabo440. Transform that Web.config!

Transformers

Right, well, if you are going to use web.config Entity Framework initialization, then you definitely want to use Web.config transformations. They just go together so well. Let's say you have a particular initialization strategy for your local dev environment - DropCreateDatabaseWhenModelChanges, so you can just blow away your database every time it changes, whereupon it gets reseeded with some minimal data. That's what I do. And obviously you want the same thing in production. Only joking. That was a joke. You want MigrateDatabaseToLatestVersion, or maybe you want to switch initialization off completely. So how do you manage the change in the web.config. Manually?

Without going into too much detail, if you're using Visual Studio to deploy your solution then don't do it manually - it's a natural fit to use Web.config transforms. But even if you aren't, I still say it's beneficial to let Visual Studio perform a transform since you can easily create a transform file and preview that transform, and make your actual testing, staging, or production Web.config from that.
"...you can see a preview of the changes in Visual Studio. In Solution Explorer, right-click the transform file (for example, Web.Release.config) and select Preview Transform. Visual Studio shows the original Web.config file and the transformed Web.config file side by side."
So, the following section is part of a transform file I use at work which switches off initialization, both by setting the disableDatabaseInitialization attribute to true on the context, and then for good measure removing the databaseInitializer node itself. The attributes starting with "xdt" belong to the XML-Document-Transform namespace: they do the transforming magic here.
<entityFramework>
  <contexts>
    <context type="MyAssembly.MyContext, MyAssembly" disableDatabaseInitialization="true" xdt:Transform="SetAttributes">
      <databaseInitializer xdt:Transform="Remove"/>
    </context>
  </contexts>
</entityFramework>
I said I wouldn't go into that much detail about config transformations because you could write a whole blog post on the ins and outs of the different types of transformations that are possible. You have to admit, anything that varies by environment ultimately should end up in one or other of your config files, and your choice of EF initialization strategy will surely vary by environment. It's such a natural fit - web.config EF initializers and web.config transformations. They're like Fred Astaire and Ginger Rogers. I really should have used a picture of ol' Fred and Ginge dancing in "Swing Time" to illustrate this point, but I just got carried away with the whole Transformers thing.