Database Migrations

Database Migrations help to maintain upgrade or downgrade the database as your application adds or removes more functionality over time. dotEntity uses classes of type IDatabaseVersion to specify versions of the database.

The following steps document how you can create database migrations.

Setting Up

In dotEntity a particular database version identified by IDatabaseVersion interface. Any class implementing this interface will represent a valid database version. The interface has two methods Upgrade and Downgrade respectively representing the upgrading and downgrading the database.

To create a new database version, we need to create a new class implementing this interface.

public class Version_1_0 : IDatabaseVersion
{
        public string VersionKey { get; }

        public void Upgrade(IDotEntityTransaction transaction)
        {
        }
        public void Downgrade(IDotEntityTransaction transaction)
        {
        }
}

As you can see, both Upgrade and Downgrade methods have IDotEntityTransaction parameter. The parameter is passed by dotEntity to run all the versioning within database transactions.

The VersionKey property identifies a unique key for the version. You can use GUID, Semantic Versioning or any other way of your choice to uniquely naming the versions.

Populating Upgrade and Downgrade methods

All the operations including Creating and Dropping tables, Creating and Dropping Constraints, Altering tables etc. are provided by DotEntity.Database class. The following methods are supported.

The methods are self explanatory and do what they are meant to do. We can use these methods to define our database version.

public class Version_1_0 : IDatabaseVersion
{
        public string VersionKey => "Version_1_0";

        public void Upgrade(IDotEntityTransaction transaction)
        {
            DotEntity.Database.CreateTables(new[]
            {
                typeof(Product),
                typeof(Category),
                typeof(ProductCategory)
            }, transaction);

            DotEntity.Database.CreateConstraint(Relation.Create<Product, ProductCategory>("Id", "ProductId"), transaction);
            DotEntity.Database.CreateConstraint(Relation.Create<Category, ProductCategory>("Id", "CategoryId"), transaction);
        }

        public void Downgrade(IDotEntityTransaction transaction)
        {
            DotEntity.Database.DropConstraint(Relation.Create<Product, ProductCategory>("Id", "ProductId"), transaction);
            DotEntity.Database.DropConstraint(Relation.Create<Category, ProductCategory>("Id", "CategoryId"), transaction);
            DotEntity.Database.DropTable<ProductCategory>(transaction);
            DotEntity.Database.DropTable<Product>(transaction);
            DotEntity.Database.DropTable<Category>(transaction);
        }
}        

The above class defines a typical class that represents a database version. The Upgrade method creates tables Product, Category and ProductCategory, after which create constraints for these tables. The Downgrade method similarly removes these tables and constraints albeit in reverse order for obvious reasons.

You can create as many database versions as you want. The Version Key must be unique for each.

Enqueue the versions

Before the migration routine can execute these database migrations, we need to enqueue the database versions in appropriate order for migration to go smoothly. This is accomplished by EnqueueVersions method of DotEntityDb class.

DotEntityDb.EnqueueVersions(new Version_1_0());
DotEntityDb.EnqueueVersions(new Version_1_1());
DotEntityDb.EnqueueVersions(new Version_2_0());

You can also pass all the versions in one call.

DotEntityDb.EnqueueVersions(new Version_1_0(), new Version_1_1(), new Version_2_0());

Note: The order in which versions are added is very important. That's where semantic versioning will make sense.

Execute the migrations

To run the migrations UpdateDatabaseToLatestVersion method of DotEntityDb should be called. The method expects a parameter callingContextName which uniquely identifies the caller of the migration. You should ideally set it to the namespace or assembly name which is executing the migration.

 DotEntityDb.UpdateDatabaseToLatestVersion("Your.Assembly.Full.Name");

The method when called will run the Upgrade method of all the database versions en-queued.

Downgrading the migrations

In order to downgrade the migrations, UpdateDatabaseToVersion should be used. The method accepts two parameters.

DotEntityDb.UpdateDatabaseToVersion("Your.Assembly.Full.Name", "Version_1_1");

The above call will run the Downgrade method of Version_2_0, so that the current database state is at Version_1_1. If instead we wanted to remove all the migrations, we'd call the method with null versionKey.

DotEntityDb.UpdateDatabaseToVersion("Your.Assembly.Full.Name", null);

Warning: While executing migrations, one must take into consideration loss of data. dotEntity doesn not check for any data while executing migrations.