EF6 'per object' deployment (Part 1)

Four more versions ... and MS might just have something!!! :wink:

Okay okay ... I'll admit it ... I'm being a little harsh.

The majority of issues I've had with Entity Framework (prior to V6!) were my hangups, a kind of friendly philosophical disagreement, sort of "artistic differences" ... akin to what a top-notch Hollywood talent might encounter, trading barbs via Variety with some money-grubbing old school studio head denying the majesty of his avant garde creativity. :wink:

But since I'm just an old dog - (a really old dog!!!) - and not really a top-notch anything ... I'll accept that the problem was mine.

I remember looking at EF when it first came out, way back in the day, and thinking (stupidly!!!) that this is a pretty cool solution in search of a problem!!!

My problem was that I was not looking through an ORM lens ... I was stuck in a classic rows/columns database rut.

In my own defense, our team was solving well-defined large (big as a buzzword hadn't been coined yet!) data problems where there was a 75/24/1 ratio of data creation to query to update.

In this world, stored procedures and views and user-defined datatypes and rules and defaults were the norm ... not the old-fashioned-and-out-of-date design concepts you young whippersnappers have the luxury of ignoring!!! :joy:

And Linq? That was just a misspelling of the small metal thingamabob used to connect together two sections of a chain. :smile:

Fast-forward to EF6
I slept through a lot of the important version churn, i.e., 4 -> 4.1 -> 4.2 -> 4.3 -> 5 1, etc., but the release of EF6 coincided with a brand new project of mine, so I thought I'd give it another look.

I read some interesting articles, found that Code First was mostly ready for prime-time, that stored procedure support had been improved, and discovered that migrations were the Cat's Meow!!! :smile:

Cool!

Yes ... true ... stored procedure and function usage was improved ... but getting straight T-SQL deployed to the database was still being treated like a red-headed stepchild.

The commonly accepted solution was to litter your Up()/Down() migration scripts with a bunch of raw DbMigration.Sql() statements, or use the lightly documented AlterStoredProcedure, CreateStoredProcedure, DropStoredProcedure, MoveStoredProcedure, RenameStoredProcedure constructs.

For a whole bunch of reasons, I found this solution ... wanting, primarily because of one of the coolest features of Code First namely, the Add-Migration command.

Here's one of the greatest understatements ever written by an underpaid MS technical writer:

Code First Migrations has two primary commands that you are going to become familiar with. 2

Familiar ... as in Biblically!!! :wink:

Once you have adapted to the EF mindset ... taken a biiiigggg loooonnnnng puuuullll on the Redmond Kool-Aid ... and Judo-like start to use its' own inertia agin it ... Code First can be pretty productive!!!

Here's the rest of the comment about the two primary commands mentioned in the above footnote:

  • Add-Migration will scaffold the next migration based on changes you have made to your model since the last migration was created

  • Update-Database will apply any pending migrations to the database

I added the emphasis to next because that is an option not a requirement. Allow me to clarify.

When I'm being productive ... really productive ... I switch back-&-forth between the application and database tiers like Michèle Mouton downshifting her Quattro through the dusty Ligurian switchbacks!!!3 :joy:

A typical wash/rinse/repeat cycle for me is to ...

  • Make a model change.
  • Push the change to the database.
  • Tab over to SSMS and create/alter the sproc I'm using which references the changed model.
  • Realize that I need the SprigBrimProng field to be a decimal, not integer.
  • Tab back to VS ... rinse/repeat.

Did I mention I'm lazy???
After typing Update-Database and Add-Migration for about the 40-gazillionth time ... I figured it was time for shortcuts.

Fortunately, as the migration infrastructure simply leverages PowerShell, it is easy to add aliases to frequently-used commands:

  1. Presuming you are already in Visual Studio 2012/2013, select the Tools | NuGet Package Manager | Package Manager Console menu option(s) to open/activate the console.
  2. Determine if a $Profile already exists:
    • At the PM> prompt, type Test-Path $Profile <enter>
      (If True is displayed, skip to #4)
  3. Create the $Profile file:
    • Type New-Item –Path $Profile –Type File –Force <enter>
      (A message will display indicating that the NuGet_profile.ps1 file was created)
  4. Edit the $Profile file:
    • Type notepad $profile <enter>
  5. Copy/Paste the following, save the changes, exit the editor, and reload the $Profile:
    • Type . $Profile <enter>
    function UpdateDatabaseResetState
    {
        Update-Database -target:0 -force
    }

    function UpdateDatabaseToStableState
    {
        Update-Database -target:Stable -force
    }

    function UpdateDatabaseToPendingState
    {
        Update-Database -target:Pending -force
    }

    function AddStableMigration
    {
        Add-Migration Stable -force
    }       

    function AddPendingMigration
    {
        Add-Migration Pending -force
    }       

    Set-Alias ud Update-Database
    Set-Alias am Add-Migration
    Set-Alias ud0 UpdateDatabaseResetState
    Set-Alias udS UpdateDatabaseToStableState
    Set-Alias udP UpdateDatabaseToPendingState
    Set-Alias amS AddStableMigration
    Set-Alias amP AddPendingMigration

    Set-Alias gmg Get-Migrations



In the very early stages of a project, right as my code-first models begin to take shape, no migrations exist.

Once I'm ready to start working with concrete data, I pop into the package manager console and type ...

AMS <enter>

... to create my first migration, which is named Stable. Presuming it passes muster, I'll then ...

UD <enter>

... to apply the changes.

That's it!

During this early phase, there's a lot of model churn. I will frequently pop into the package manager console and enter:

  • UD0 <enter> to return the database to empty
  • AMS <enter> to recreate the Stable migration.
  • UD <enter> to apply the changes to the database

This is the wash/rinse/repeat cycle. Easy-peazy! :smile:

But wait ... there's more ... call now ... operators are standing by ...
At any given point in a project lifecycle, some things will be pretty-much-darn-close-to-done :wink: while others are just starting. I've taken to calling the former stable and the latter pending (ergo the method names/abbreviations in the aliases).

I create the Pending migration by entering AMP at the console, and I can do that as often as I want ... right up until the time I execute the next UD command.

Sometimes I'll get ahead of myself, forgot that I recently did a UD and then do another change followed by another AMP. Ooops, that'll result in the creation of a Pending1.cs migration file. Gosh ... if there was only another solution ... :smile:

Analogous to the UD0 operation is the UDS alias; this will result in rolling back the database state to the Stable migration rather than all the way back to the beginning.

(But wait ... don't do that immediately ... you need to first delete the errant Pending1.cs migration file!!!)

BTW, I'm intentionally glossing over the fact that the name of a migration file always begin with a full year/month/day/hour/minute prefix ...

Once the Pending1.cs migration file has been deleted, run the UDS command, followed by AMP to update the existing Pending.cs migration file, and then UD to apply the changes.

Easy-peazy ... ditto!!! :wink:

As coding progresses, Pending trends towards completion and becomes a candidate for promotion into the Stable state. Here's how that's done:

  • UD0 to return the database to empty state.
  • Delete the Pending.cs migration file.
  • AMS to recreate the Stable migration (which now will include the entire model)
  • UD to apply the changes.
  • Profit!



A note about Automatic Migrations...
At the bottom of the same Code First MSDN article there is a brief discussion about the MigrateDatabaseToLatestVersion Initializer4.

I have a love/hate relationship with this feature: during times of rapid iteration, it is certainly helpful to just let EF6 do it's thing. But I'm also guilty of sometimes leaving this initializer active and then losing track of what changes went with what feature and when did I do it, etc. This is a real case of YMMV, but the ability to rollback all changes and bundle the current state into either the Stable or Pending migration certainly reduces risk and therefore my hesitation to use this.

So anyway ...
While it may seem like I've forgotten the title of this blog post and have just been blathering on about EF6 migrations ... there's about a teaspoon-full of method to my madness!

But you're just gonna have to wait for Part 2 before it will become a wee bit more apparent ... :grin:


Comments powered by Disqus