I am using continuous integration with TeamCity, NUnit, and Git. I recently migrated (pardon the pun) from FluentMigrator to Entity Framework Migrations. I primarily did this to take advantage of the scaffolding functionality therein.
However, it is potentially possible to check in some changes to source control without having first scaffolded the changes into migrations (imagine the scenario where the application was not run before committing and pushing the commit). I am using a pre-tested commit workflow, so I would like to detect this problem in the pre-test rather than waiting until calling migrate.exe (which would be too late in my workflow and break the "green repository").
My question is, how to create a unit/integration test to detect when the migrations model doesn't match the context model so I can fail the build before attempting to migrate? Do note that I expect it not to match the database and would prefer not to access the database from the test.
I tried this approach:
[Test]
public void CheckWhetherEntityFrameworkMigrationsContextIsUpToDate()
{
Assert.DoesNotThrow(() =>
{
CallDatabase();
});
}
private void CallDatabase()
{
using (var ctx = new MyContext("SERVER=(local);DATABASE=MyDatabase;Integrated Security=True;"))
{
var tenant = (from t in ctx.Tenant
select t).FirstOrDefault();
}
}
But this will always fail when there are pending migrations (rather than just in the case where the migrations model is not in sync with the context model, which is what I am after).
I have added a work item on the EntityFramework project for this issue. Hopefully, they will look into adding a way to do this.
Here is some code that will check whether all model changes have been scaffolded to migrations or not.
bool HasPendingModelChanges()
{
// NOTE: Using MigratorScriptingDecorator so changes won't be made to the database
var migrationsConfiguration = new Migrations.Configuration();
var migrator = new DbMigrator(migrationsConfiguration);
var scriptingMigrator = new MigratorScriptingDecorator(migrator);
try
{
// NOTE: Using InitialDatabase so history won't be read from the database
scriptingMigrator.ScriptUpdate(DbMigrator.InitialDatabase, null);
}
catch (AutomaticMigrationsDisabledException)
{
return true;
}
return false;
}