CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jacob Lewallen

April 2008 - Posts

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

  • Good Tools and ReSharper 4 EAP Thoughts

    One idea I've been thinking about lately is that good tools get out of your way. I started thinking about this after reading an article about photography and fancy equipment. A few days later I was upgrading my ReSharper to the latest EAP and started thinking about how the same principal applies to my tools in software development.

    I've been a huge fan of ReSharper for a long time. It and ViEmu are an absolute necessity for me when doing any kind of work with C#. After we first installed it at work we would joke that it just knows what you want to do. When I use a new tool I usually don't see the value right away and to help others see the value faster I'm going to give a short list of the features I personally use constantly. Here is my list for ReSharper:

    1. Error Highlighting: Anything that saves me from a failed build saves me tons of time.
    2. Quick-Fix (Alt-Enter): Import Type and Initialize field from constructor(s) parameter. I use one of these every minute it seems. I also like that I can disable things. For example, I disable "Redundant this qualifier", that's how I roll.
    3. Generate (Alt-Ins): I usually generate properties and constructors with field initializers.
    4. Refactor: Move Type to File, Rename
    5. Ctrl-Alt-Space: Complete Type Name from any namespace, imported or not.
    6. Find Usages: I like that it keeps a history of past searches and that I can group things by project/namespace. It crosses usages out when I change things too, which is helpful.

    It can do a lot more than this. What's particularly important is that I never know I'm using another tool. I rarely see dialogs popup and break my flow, that sort of thing. It gets out of my way. I'm always looking at my code. That's important because it's what I'm doing. Do your favorite tools get out of your way? While I have an audience I'm going to ask for a couple of things:

    1. I would love to be able to set my IntelliSense to default to Ctrl-Alt-Space mode when completing a type name. I'm trying to avoid having to go into that mode manually.
    2. When generating code (properties, constructors, etc...) there's a second dialog now that lets you set properties on the generated members. I would like to disable this second dialog, I'll never use it. It's much faster for me to manually tweak the generated code using my Vim-Fu.

    Are either of these things possible already? Also, before you go, I think it's good to know which specific things people find particularly useful about their tools. If you have any features of any tool that you use several times a day, I'd love to hear what they are. Maybe I'm missing out on something.

    Also, I'll be in Seattle for ALT.NET so come up and shake my hand!

  • Projects in Visual Studio

    machine-projects

    In the world of Visual Studio we have solutions and projects. I create lots of projects. At Eleutian we have close to 40 projects in our main solution. Keep in mind, that at any given time I only have a subset those loaded depending on which feature I am working on. Machine, a relatively recent undertaking, is growing and is already up to 13 projects. I've been thinking of the higher level organization of my software development process and come to some conclusions. As I'm working on a piece of software there are two primary reasons for why I create a new project:

    1. Do I need a place to store tests for another project? Nearly half of all the projects I create are test projects. :)
    2. Do I have a group of classes that can be packaged and then later re-used? I see this situation as an application of Separation of Concerns.

    Number two is particularly important. Projects to me are a useful way to create distinct, reusable collections of code. Separate projects are good ways of isolating heavy dependencies. Storing static code in a separate project is a good way to decrease time spent during rebuilds. In a client-server situation it's handy to place code common to both tiers in a shared project. Projects are wonderful tools for good design and lately I've been wishing I had more control over them.

    Visual Studio enforces a strict one assembly per project convention. Remember, we can change preprocessing settings, target CPU, debug/optimization settings, and a few other things via Build Configurations. In my experience the one-to-one convention creates some problems:

    1. A lot of project configuration is duplicated. Most of my projects have the same core build configuration and only a few are different and then only in minor ways. I've often wished I could create my Build Configurations and then reference them from my projects.
    2. They get in the way! For me, projects are usually created once and forgotten. Most of my project-level changes are rare. Yet, they are everywhere in the user interface. Thankfully with R# I can GotoType around them, but I still feel like they stand between me and the code. I'm weird that way I guess. My focus is the code, not the projects.
    3. In order to build or use code under two different project-configurations I'm usually required to perform some kind of trickery. Refactoring code into a common project that is referenced by two others, for example. I've even had to create two csproj's in one directory that both include the same code, this is awkward.

    I want two things. I want something slightly lower on the hierarchy than a project - a sub-project. We could, for example, group tests into a sub project and the production code into another. Much of the existing project configuration would be shared among them, including dependencies and the active build configuration. Additionally, and this is huge, I would get rid of the parent-child relationship between projects and code. Projects would become external configuration that would be applied to sets of code.

    I used to do lots of Java development and because of the atomic nature of the .class files this kind of structure was very easy. A single project can result in multiple jar files from which classes can be included/excluded at will. I'm really proposing a more shallow solution hierarchy. How sub-projects would look in VS I can't say. Thankfully, this is not a limitation of C# or .NET. The fact is, we can probably achieve a very rudimentary implementation of this via some clever tweaking of the standard .targets files.

    I'm curious about how much I would like this approach in practice. You never know how much you'll like something until you've tried it. I do know that when I used Java I didn't wish for the VS approach. Any thoughts? Is this too much? Is this not enough?

More Posts