Jacob Lewallen

Sponsors

The Lounge

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
A First Look At Machine.Migrations

Introduction

We used ActiveRecord migrations at Eleutian for a number of months. Everything was right with the world and we had no major complaints until we started running into a problem with our more complex migration scenarios. Usually, we see two kinds of migrations:

  • Schema - Add/remove/rename columns, manipulate tables, and general schema related changes.
  • Data - Refactoring data into new schemas and major data reorganization or population.

Schema migrations are handled great by most of the migration frameworks out there. One problem we were running into was it's typically easier to write the data migrations in C# directly using our entities and services. As a result we had two different ways of migrating and this made our frequent publishes problematic. We decided to consolidate the two operations.

On the outside there's nothing really new in Machine.Migrations that you haven't seen elsewhere. Most of the changes are internal. Migrations are numbered right now, although we've talked of moving to time-stamping them a la the newer ruby ActiveRecord. What Machine.Migrations does allow you to do is heavily tweak your migrations to fit your project. In our project we have a custom MsBuild task that extends the default implementation to provide access to our DaoFactory and other goodies. If this is something that interests you, then read on. If you're happy with your current migration scenario then you probably don't have a huge need for Machine.Migrations.

A Simple Migration

I'll start with the actual migrations. Here is an example that creates a table:

using System;
using System.Collections.Generic;
using Machine.Migrations;

public class CreateUserTable : SimpleMigration
{
  public override void Up()
  {
    Schema.AddTable("users", new Column[] {
      new Column("Id", typeof(Int32), 4, true, false),
      new Column("Name", typeof (string), 64, false, false),
      new Column("Email", typeof (string), 64, false, false),
      new Column("Login", typeof (string), 64, false, false),
      new Column("Password", typeof (string), 64, false, false),
    });
  }

  public override void Down()
  {
    Schema.DropTable("users");
  }
}

Machine.Migrations does enforce a file naming/class naming restriction so the file for this migration would be named 001_create_user_table.cs. We inherit from SimpleMigration, which is built-in and provides access to Schema manipulations and raw Database queries through those properties respectively. We have a solution and project for our migrations so that we can get IntelliSense while writing them. This can be helpful if you forget the syntax.

When you run Machine.Migrations it will only compile the migrations that it needs to apply. This is very handy if you are doing high-level migrations including your entities because then you don't have to maintain them after they've been applied to your live database. But, this does mean you won't be able to migrate from the beginning to the end because migrations will become outdated. This has been a minor problem for us because we snapshot our live database and run that in development. I do think a better long term solution is required though, in order to make creating a brand new database easier. Perhaps flagging migrations as data migrations and not running them if they don't compile? I am not sure.

There is also a Boo migration factory that you can use if you don't want to write your migrations in C#. Internally, the infrastructure is there if you want to add other languages as well.

Another interesting thing I should note is that each migration is run inside its own transaction, so if you mess up it'll just rollback. This is very nice under SQL Server because schema changes are rolled back as well.

How Do I Start Using It?

If you just want to use Machine.Migrations out of the box (a good first step) it's fairly straightforward:

  1. Copy Machine.Migrations and its dependencies into your project. Be sure to include the Machine.Migrations.targets file which has the default MsBuild task and target.
  2. You can then drop the following XML into your MsBuild file (check all your paths!):
    <Import Project="Libraries\Machine\Machine.Migrations.targets" />
    
    <PropertyGroup>
      <MigrationConnectionString>Data Source=127.0.0.1;Initial Catalog=MyDb;Integrated Security=SSPI</MigrationConnectionString>
    </PropertyGroup>
    
    <Target Name="Migrate" DependsOnTargets="MigrateDatabase">
    </Target>
  3. All that's happening here is we're importing the targets file, defining a connection string to use when migrating, and then making a target that depends on the target we imported.
  4. By default it will look for your migrations in a subdirectory named Migrations.
  5. Then it's just a matter of running MsBuild YourProject.proj /t:Migrate where YourProject.proj is your MsBuild file.

One thing you may notice is that sometimes your migrations will fail to compile because of a missing dependency. In order to ensure your own or third party assemblies are referenced you add them to the MigrationReferences ItemGroup, like so:

<ItemGroup>
  <MigrationReferences Include="System.Xml.dll">
    <InProject>false</InProject>
  </MigrationReferences>
</ItemGroup>

When Machine.Migrations compiles it will include those assemblies in the references list.

What Next?

I'm going to hold off on explaining how to extend the default migration scenario until later because this post is already pretty long. For anyone feeling ambitious it basically amounts to extending SimpleMigration in your own code and inheriting from that. (Be sure to include your assembly in MigrationReferences). We go a step further and wrap the MsBuild task as well, which may or may not be necessary for you.

I can't stress enough that going down this road isn't for everybody. There is an argument for keeping migrations simple and making those complex data migrations something else's concern. For now, this is what we're doing and it has worked nicely. I'm sure there will be lots of questions as well, please feel free to comment and offer up suggestions. As far as I know we're the only people using this code, so I'm sure there are quirks.


Posted Fri, Apr 25 2008 1:27 PM by jlewallen
Filed under: , , ,

[Advertisement]

Comments

Jacob wrote re: A First Look At Machine.Migrations
on Fri, Apr 25 2008 10:17 PM

David,

I think you'd have to migrate your mappings and have that generate your schema migrations. Otherwise I'm not sure you could infer enough from the schema changes to adjust the mappings. Interesting idea though.

Whenever I start thinking about improving migrations in general I get frustrated. I just don't like migrations at all. I want my application to migrate itself when I publish.... I've had ideas on this but nothing worth implementing just yet, especially given our architecture.

Andrew Stewart wrote re: A First Look At Machine.Migrations
on Sun, Apr 27 2008 2:14 PM

Hi

I'm just wondering how you got the version number from the filename? As a contributer to rikMigrations a similar framework I'm unaware of a way you can do this? We have used a migration attribrute on ours t get the same effect however it is code noise.

Cheers

Andy

jlewallen wrote re: A First Look At Machine.Migrations
on Sun, Apr 27 2008 3:29 PM

Andrew,

Machine.Migrations looks for individual files matching a regular expression and pulls the version number out then. It creates MigrationReference from that process. (I've included a link to the relevant source)  Once it has decided which migrations to apply (based on those versions and the version in the database) they are then compiled (See MigrationRunner.CanMigrate) Each individual migration gets its own assembly. We never lose that original MigrationReference and so we know where to find each versions migration, etc...

svn2.assembla.com/.../MigrationFinder.cs

svn2.assembla.com/.../MigrationRunner.cs

jlewallen wrote re: A First Look At Machine.Migrations
on Sun, Apr 27 2008 3:35 PM

Oops, I am a total moron and accidentally deleted some comments when trying to nuke that spam. Here is what I nuked, sorry David.

> David Laribee said:

Cool.

I half wonder if migrations shouldn't refactor NH mappings as well. In a greenfield scenario where you've got a clean association of tables serving as entity and value object stores, why not?

> And then in reply to my 7:17pm comment:

I got the idea talking to Jeremy Miller about an internal DSL (fluent interface) for expressing NHibernate mappings. Pairing this idea with a sort of "conventional mapping" approach (where you don't have to express all that much mapping at all; it is inferred) got me wondering about why we're migrating our databases then modifying our mappings, especially (or exclusively) in a greenfield or highly-controlled, model-first scenario.

So now I return to the idea of why we don't maintain migrations as maps and why we don't treat maps as evolutionary. This style would seem possible given the whole object first mentality a lot of us are latched on to.

Reflective Perspective - Chris Alcock » The Morning Brew #82 wrote Reflective Perspective - Chris Alcock &raquo; The Morning Brew #82
on Mon, Apr 28 2008 3:31 AM

Pingback from  Reflective Perspective - Chris Alcock  &raquo; The Morning Brew #82

Andrew Stewart wrote re: A First Look At Machine.Migrations
on Mon, Apr 28 2008 2:40 PM

Hi Jacob

Thanks for that, thats just cheating compiling it all together when you need it. ;o)

Cheers

Andy

Colin Jack wrote re: A First Look At Machine.Migrations
on Mon, Apr 28 2008 3:55 PM

Very cool stuff, look forward to reading your future blog posts on it.

Colin Jack wrote re: A First Look At Machine.Migrations
on Mon, Apr 28 2008 4:29 PM

Realized I have a few questions but first I should say that since I have little experience of the migration functionality in Rails this could be a dumb question...

Basically I wondered what would push you towards this approach versus normal change scripts? I'm just asking as we use manually created SQL files, they and the resulting DB are versioned so we could apply just the required updates but in general we just build the DB from scratch on dev machines.

Anyway I'm guessing that the main advantage of this sort of approach is when you go to the more complex model driven approaches (based on your NHibernate entities)?

Chris Bilson wrote re: A First Look At Machine.Migrations
on Tue, Apr 29 2008 9:17 AM

Hi Jacob,

I have been playing around with IronPython and since there is already visual studio integration for it and the syntax is so similar to boo, I figured it would be fun to add IPy support to Machine.Migrations.

I did that, and while the language is similar to boo, the API is completely different. Basically, instead of compiling a module, you ask the IPy engine for types defined in migrations scripts.

I have the implementation of a PythonMigrationFactory along with a test. I can submit a patch if you are interested in looking at it.

Until boo support for visual studio gets built, this is a simple way to get vs integration.

jlewallen wrote re: A First Look At Machine.Migrations
on Tue, Apr 29 2008 12:15 PM

Chris, absolutely, send it on over!

Colin,

Without knowing more about the particular system you're using I'm not sure I can offer up all of the pros and cons. If you're using just SQL change scripts the pros I can think of are you're far more resilient to changes. As I mentioned our particular approach doesn't do so well when we let data migrations sit around without being applied because you need to maintain them. And then you lose the ability to build your DB from scratch. Then again, using this approach gives you power. You can do lots of things in your migrations that would normally be very difficult or error prone in SQL. Error prone because we do use NH and the SQL tends to be pretty complex in inheritance mapped hierarchies.

We also run production copies of the DB in dev so we're more aware of the performance. This solves the problem with old data migrations that won't compile and us from having to create new blank databases. We run our tests against a light version of the database that is basically production with data purged.

I'm a big believer in tools being right for some and not necessarily right for others.

Colin Jack wrote re: A First Look At Machine.Migrations
on Tue, Apr 29 2008 2:23 PM

Ta for replying.

"Error prone because we do use NH and the SQL tends to be pretty complex in inheritance mapped hierarchies."

Yeah that's definitely true, will be interested in reading more about the approach that builds on the NHibernate/entities.

Zen and the art of Castle maintenance » Blog Archive » Machine.Migrations is freaking sweet wrote Zen and the art of Castle maintenance &raquo; Blog Archive &raquo; Machine.Migrations is freaking sweet
on Thu, May 22 2008 4:15 PM

Pingback from  Zen and the art of Castle maintenance  &raquo; Blog Archive   &raquo; Machine.Migrations is freaking sweet

Germán Schuager wrote re: A First Look At Machine.Migrations
on Fri, May 30 2008 10:55 AM

Hi, I've been experimenting with an addition to Machine.Migrations to make it available as a NAnt task.

One of the problems I've found along the way is that I Machine.Core is needed to reference NAnt.Core which is not signed, so in order to continue I've had to disable the signing on the resulting assemblies.

I can submit a patch if you are interested.

aaronjensen wrote re: A First Look At Machine.Migrations
on Fri, May 30 2008 3:15 PM

Why does Machine.Core need to reference NAnt.Core? We should probably put build tasks in a different assembly if the assemblies they need to reference won't be signed. Disabling signing is not an option.

That said, I know hammett is also working on a nant task, so we should probably get one of your implementations in so people don't have to do it on their own :)

Please send me your patch to aaronzjensen at gmailz.com (remove the z's)

A First Look At Machine.Migrations [ CodeBetter ] wrote A First Look At Machine.Migrations [ CodeBetter ]
on Wed, Jun 18 2008 10:18 AM

Pingback from  A First Look At Machine.Migrations [ CodeBetter ]

Mike wrote re: A First Look At Machine.Migrations
on Tue, Jul 1 2008 5:30 PM

Hi,

I have two questions:

1. Where can I find a download for Machine.Migrations?

2. How does it compare to SubSonic Migrations and Migrator.Net?

Thanks for the introduction!

Eden Ridgway's Blog wrote Are Database Migration Frameworks Worth the Effort?
on Sat, Jan 3 2009 6:52 AM

Are Database Migration Frameworks Worth the Effort?

Bryan Czapp wrote Machine.Migrations: Version Tracking for Database Schemas
on Sat, Mar 7 2009 8:41 AM

Machine.Migrations: Version Tracking for Database Schemas

Aaron Jensen wrote Machine.Migrations changes
on Sat, Oct 24 2009 5:56 PM

Jacob first introduced Machine.Migrations over a year ago. Since then, it's been a solid part of