views:

29

answers:

1

Hi,

Background - I need a framework/approach to managed database updates for a .NET Winforms app being deployed on users PC's via clickonce deploy. The app uses a sqlite database.

Q1. What mechanism does Subsonic use to run such migrations on the local PC? e.g. would it be MSBuild

Q2. If it does need a tool like how can my application robustly kick off MsBuild? i.e. how can it be sure what path it is installed, what if it is not installed, should I be including the MSBuild.exe in the clickonce package so that I know it is there for sure myself?

Q3. Any other suggestions on how to use Subsonic in this specific use case?

Q4. Any comments on whether MigratorDotNet would be a better fit? (if someone has had experience with both)

Q5. Could I use subsonic's bare migration framework and just have a set of SQL files to do the upgrade/downgrade? i.e. just use the framework to check database version and which scripts to run etc?

+1  A: 

Well, the post is a little old but maybe my answers are still of use.

Q1: SubSonic Migrations are code files that are complied and executed at runtime by subcommander (sonic.exe) which means the code files have to be on disk and must follow the naming convention 001_migration_name.cs for subcommander to know the execution order.

Q2: You don't need msbuild for migrations. The only thing you need is the sonic.exe and it's dependencies.

Q3: It is possible (and not very hard) to create your own logik to execute the migrations in your project at runtime without using subcommander.

I basically find every class that is derived from Migration and (since a class cannot start with a number) my convention is that the last 3 digits from the class name are the migration number (like the 001_migration_name.cs my classes are defined as Migration001 : Migration)

public class MigrationHelper
{
    private const string providerName = "MyProviderName";
    public static int CurrentVersion { get { return SubSonic.Migrations.Migrator.GetCurrentVersion(providerName); } }

    private static Dictionary<int, Migration> _migrations;
    public static Dictionary<int, Migration> Migrations
    {
        get
        {

            if (_migrations == null)
            {
                _migrations = new Dictionary<int, Migration>();
                foreach (Type t in new MigrationHelper().GetType().Assembly.GetExportedTypes())
                {
                    if (t.BaseType == typeof(SubSonic.Migration))
                    {
                        int number;
                        if (int.TryParse(t.Name.Substring(t.Name.Length - 3, 3), out number))
                            Migrations.Add(number, (Migration)Activator.CreateInstance(t));
                        else
                            throw new InvalidOperationException("Classes that inherit SubSonic Migrations have to be named MigrationXXX where x is the unique migration number");
                    }
                }
            }
            return _migrations;

        }
    }

    public static void ExecuteMigration(Migration m, Migration.MigrationDirection direction)
    {
        m.Migrate(providerName, direction);
    }


}

In your programm you can determine the current version by MigrationHelper.CurrentVersion and then execute every single migration from current to max (if you wanne go up) or some other number. Here is how you use it.

        Migration m = MigrationHelper.Migrations[15];
        MigrationHelper.ExecuteMigration(m, Migration.MigrationDirection.Up);

Q4: I don't have experience with MigratorDotNet but if your app uses subsonic than the subsonic migrations are a good choise since you don't need to deploy any additional libs.

Q5: You can use subsonic migrations for that. Just do:

Execute("CREATE TABLE ...");

in the Up() or Down() method. But the advantage of using the predefined methods are (besides the fact that they work across multiple dbms' which is propable not so importent if you only use sqlite) is that you have some pre flight checks (e.g. a migration will fail if you define a field twice before the actual sql is executed)

SchlaWiener