Adds ability to have proxy migrations so there is no duplication of declaring migrations and no duplication of executing already executed migrations. Would have been a bit nicer to have

This commit is contained in:
Shannon
2014-03-12 20:36:40 +11:00
parent 3b26834484
commit 0dc00a62e2
13 changed files with 226 additions and 116 deletions

View File

@@ -275,7 +275,7 @@ namespace Umbraco.Core
//the database migration objects
MigrationResolver.Current = new MigrationResolver(
() => PluginManager.Current.ResolveMigrationTypes());
() => PluginManager.Current.ResolveTypes<IMigration>());
// todo: remove once we drop IPropertyEditorValueConverter support.
PropertyEditorValueConvertersResolver.Current = new PropertyEditorValueConvertersResolver(

View File

@@ -6,7 +6,7 @@ namespace Umbraco.Core.Persistence.Migrations
/// Represents the Migration attribute, which is used to mark classes as
/// database migrations with Up/Down methods for pushing changes UP or pulling them DOWN.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class MigrationAttribute : Attribute
{
public MigrationAttribute(string targetVersion, int sortOrder, string product)

View File

@@ -27,26 +27,8 @@ namespace Umbraco.Core.Persistence.Migrations
/// </summary>
public IEnumerable<IMigration> Migrations
{
get { return GetSortedValues(); }
get { return Values; }
}
/// <summary>
/// Override how we determine object weight, for this resolver we use the MigrationAttribute attribute
/// </summary>
/// <param name="o"></param>
/// <returns></returns>
protected override int GetObjectWeight(object o)
{
var type = o.GetType();
var attr = type.GetCustomAttribute<MigrationAttribute>(true);
return attr == null ? DefaultPluginWeight : attr.SortOrder;
}
protected override int DefaultPluginWeight
{
get { return 0; } //set's the default to 0
set { base.DefaultPluginWeight = value; }
}
}
}

View File

@@ -51,27 +51,15 @@ namespace Umbraco.Core.Persistence.Migrations
? OrderedUpgradeMigrations(foundMigrations).ToList()
: OrderedDowngradeMigrations(foundMigrations).ToList();
//SD: Why do we want this?
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _configuredVersion, _targetVersion, true), this))
return false;
//Loop through migrations to generate sql
var context = new MigrationContext(databaseProvider, database);
foreach (MigrationBase migration in migrations)
{
if (isUpgrade)
{
migration.GetUpExpressions(context);
LogHelper.Info<MigrationRunner>(string.Format("Added UPGRADE migration '{0}' to context", migration.GetType().Name));
}
else
{
migration.GetDownExpressions(context);
LogHelper.Info<MigrationRunner>(string.Format("Added DOWNGRADE migration '{0}' to context", migration.GetType().Name));
}
}
var context = ExecuteMigrations(migrations, database, databaseProvider, isUpgrade);
//Transactional execution of the sql that was generated from the found migrations
using (Transaction transaction = database.GetTransaction())
using (var transaction = database.GetTransaction())
{
int i = 1;
foreach (var expression in context.Expressions)
@@ -96,32 +84,73 @@ namespace Umbraco.Core.Persistence.Migrations
return true;
}
internal MigrationContext ExecuteMigrations(List<IMigration> migrations, Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
{
//Loop through migrations to generate sql
var context = new MigrationContext(databaseProvider, database);
foreach (var migration in migrations)
{
var baseMigration = migration as MigrationBase;
if (baseMigration != null)
{
if (isUpgrade)
{
baseMigration.GetUpExpressions(context);
LogHelper.Info<MigrationRunner>(string.Format("Added UPGRADE migration '{0}' to context", baseMigration.GetType().Name));
}
else
{
baseMigration.GetDownExpressions(context);
LogHelper.Info<MigrationRunner>(string.Format("Added DOWNGRADE migration '{0}' to context", baseMigration.GetType().Name));
}
}
else
{
//this is just a normal migration so we can only call Up/Down
if (isUpgrade)
{
migration.Up();
LogHelper.Info<MigrationRunner>(string.Format("Added UPGRADE migration '{0}' to context", migration.GetType().Name));
}
else
{
migration.Down();
LogHelper.Info<MigrationRunner>(string.Format("Added DOWNGRADE migration '{0}' to context", migration.GetType().Name));
}
}
}
return context;
}
internal IEnumerable<IMigration> OrderedUpgradeMigrations(IEnumerable<IMigration> foundMigrations)
{
var migrations = (from migration in foundMigrations
let migrationAttribute = migration.GetType().FirstAttribute<MigrationAttribute>()
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _configuredVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
select migration);
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _configuredVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
select migration).Distinct();
return migrations;
}
public IEnumerable<IMigration> OrderedDowngradeMigrations(IEnumerable<IMigration> foundMigrations)
{
var migrations = (from migration in foundMigrations
let migrationAttribute = migration.GetType().FirstAttribute<MigrationAttribute>()
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _configuredVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
select migration);
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _configuredVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
select migration).Distinct();
return migrations;
}

View File

@@ -529,16 +529,7 @@ namespace Umbraco.Core
{
return ResolveTypesWithAttribute<BaseMapper, MapperForAttribute>();
}
/// <summary>
/// Returns all available IMigrations in application
/// </summary>
/// <returns></returns>
internal IEnumerable<Type> ResolveMigrationTypes()
{
return ResolveTypes<IMigration>();
}
/// <summary>
/// Returns all SqlSyntaxProviders with the SqlSyntaxProviderAttribute
/// </summary>

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.ObjectResolution;
@@ -24,18 +25,19 @@ namespace Umbraco.Tests.Migrations
{
typeof (AlterUserTableMigrationStub),
typeof(Dummy),
typeof (FourNineMigration),
typeof (FourTenMigration),
typeof (FourElevenMigration)
typeof (SixZeroMigration1),
typeof (SixZeroMigration2),
typeof (FourElevenMigration),
typeof (FiveZeroMigration)
});
Resolution.Freeze();
SqlSyntaxContext.SqlSyntaxProvider = SqlCeSyntax.Provider;
}
}
[Test]
public void Can_Find_Migrations_With_Targtet_Version_Six()
public void Can_Find_Migrations_With_Target_Version_Six()
{
var foundMigrations = MigrationResolver.Current.Migrations;
var targetVersion = new Version("6.0.0");
@@ -56,8 +58,9 @@ namespace Umbraco.Tests.Migrations
Assert.That(list.Count, Is.EqualTo(3));
var context = new MigrationContext(DatabaseProviders.SqlServerCE, null);
foreach (MigrationBase migration in list)
foreach (var migration1 in list)
{
var migration = (MigrationBase) migration1;
migration.GetUpExpressions(context);
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.Migrations;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions;
namespace Umbraco.Tests.Migrations
{
[TestFixture]
public class MigrationRunnerTests
{
[Test]
public void Executes_Only_One_Migration_For_Spanning_Multiple_Targets()
{
var runner = new MigrationRunner(new Version(4, 0, 0), new Version(6, 0, 0), "Test");
var migrations = runner.OrderedUpgradeMigrations(new List<IMigration> {new MultiMigration()});
var ctx = runner.ExecuteMigrations(
//new List<IMigration> {new DoRunMigration(), new DoNotRunMigration()},
migrations.ToList(),
new Database("umbracoDbDSN")
, DatabaseProviders.SqlServerCE, true);
Assert.AreEqual(1, ctx.Expressions.Count());
}
[Test]
public void Executes_Migration_For_Spanning_One_Target_1()
{
var runner = new MigrationRunner(new Version(4, 0, 0), new Version(5, 0, 0), "Test");
var migrations = runner.OrderedUpgradeMigrations(new List<IMigration> { new MultiMigration() });
var ctx = runner.ExecuteMigrations(
//new List<IMigration> {new DoRunMigration(), new DoNotRunMigration()},
migrations.ToList(),
new Database("umbracoDbDSN")
, DatabaseProviders.SqlServerCE, true);
Assert.AreEqual(1, ctx.Expressions.Count());
}
[Test]
public void Executes_Migration_For_Spanning_One_Target_2()
{
var runner = new MigrationRunner(new Version(5, 0, 1), new Version(6, 0, 0), "Test");
var migrations = runner.OrderedUpgradeMigrations(new List<IMigration> { new MultiMigration() });
var ctx = runner.ExecuteMigrations(
//new List<IMigration> {new DoRunMigration(), new DoNotRunMigration()},
migrations.ToList(),
new Database("umbracoDbDSN")
, DatabaseProviders.SqlServerCE, true);
Assert.AreEqual(1, ctx.Expressions.Count());
}
[Migration("6.0.0", 1, "Test")]
[Migration("5.0.0", 1, "Test")]
private class MultiMigration : MigrationBase
{
public override void Up()
{
Context.Expressions.Add(new AlterColumnExpression());
}
public override void Down()
{
Context.Expressions.Add(new AlterColumnExpression());
}
}
}
}

View File

@@ -0,0 +1,16 @@
using Umbraco.Core.Persistence.Migrations;
namespace Umbraco.Tests.Migrations.Stubs
{
[Migration("5.0.0", 0, "Test")]
public class FiveZeroMigration : MigrationBase
{
public override void Up()
{
}
public override void Down()
{
}
}
}

View File

@@ -0,0 +1,18 @@
using Umbraco.Core.Persistence.Migrations;
namespace Umbraco.Tests.Migrations.Stubs
{
[Migration("4.11.0", 0, "Test")]
public class FourElevenMigration : MigrationBase
{
public override void Up()
{
Alter.Table("umbracoUser").AddColumn("companyPhone").AsString(255);
}
public override void Down()
{
Alter.Table("umbracoUser").AlterColumn("regularPhone").AsString(255);
}
}
}

View File

@@ -1,46 +0,0 @@
using Umbraco.Core.Persistence.Migrations;
namespace Umbraco.Tests.Migrations.Stubs
{
[MigrationAttribute("6.0.0", 1, "Test")]
public class FourNineMigration : MigrationBase
{
public override void Up()
{
Alter.Table("umbracoUser").AddColumn("secret").AsString(255);
}
public override void Down()
{
Alter.Table("umbracoUser").AlterColumn("passwordTip").AsString(100);
}
}
[MigrationAttribute("6.0.0", 2, "Test")]
public class FourTenMigration : MigrationBase
{
public override void Up()
{
Alter.Table("umbracoUser").AddColumn("secondEmail").AsString(255);
}
public override void Down()
{
Alter.Table("umbracoUser").AlterColumn("sendEmail").AsBoolean();
}
}
[MigrationAttribute("4.11.0", 0, "Test")]
public class FourElevenMigration : MigrationBase
{
public override void Up()
{
Alter.Table("umbracoUser").AddColumn("companyPhone").AsString(255);
}
public override void Down()
{
Alter.Table("umbracoUser").AlterColumn("regularPhone").AsString(255);
}
}
}

View File

@@ -0,0 +1,18 @@
using Umbraco.Core.Persistence.Migrations;
namespace Umbraco.Tests.Migrations.Stubs
{
[MigrationAttribute("6.0.0", 1, "Test")]
public class SixZeroMigration1 : MigrationBase
{
public override void Up()
{
Alter.Table("umbracoUser").AddColumn("secret").AsString(255);
}
public override void Down()
{
Alter.Table("umbracoUser").AlterColumn("passwordTip").AsString(100);
}
}
}

View File

@@ -0,0 +1,18 @@
using Umbraco.Core.Persistence.Migrations;
namespace Umbraco.Tests.Migrations.Stubs
{
[Migration("6.0.0", 2, "Test")]
public class SixZeroMigration2 : MigrationBase
{
public override void Up()
{
Alter.Table("umbracoUser").AddColumn("secondEmail").AsString(255);
}
public override void Down()
{
Alter.Table("umbracoUser").AlterColumn("sendEmail").AsBoolean();
}
}
}

View File

@@ -157,6 +157,10 @@
<Compile Include="Membership\DynamicMemberContentTests.cs" />
<Compile Include="Membership\MembershipProviderBaseTests.cs" />
<Compile Include="Membership\UmbracoServiceMembershipProviderTests.cs" />
<Compile Include="Migrations\MigrationRunnerTests.cs" />
<Compile Include="Migrations\Stubs\FiveZeroMigration.cs" />
<Compile Include="Migrations\Stubs\FourElevenMigration.cs" />
<Compile Include="Migrations\Stubs\SixZeroMigration2.cs" />
<Compile Include="MockTests.cs" />
<Compile Include="Models\UmbracoEntityTests.cs" />
<Compile Include="Mvc\UmbracoViewPageTests.cs" />
@@ -253,7 +257,7 @@
</Compile>
<Compile Include="Migrations\Stubs\AlterUserTableMigrationStub.cs" />
<Compile Include="Migrations\Stubs\Dummy.cs" />
<Compile Include="Migrations\Stubs\FourNineMigration.cs" />
<Compile Include="Migrations\Stubs\SixZeroMigration1.cs" />
<Compile Include="Migrations\TargetVersionSixthMigrationsTest.cs" />
<Compile Include="Migrations\Upgrades\MySqlUpgradeTest.cs" />
<Compile Include="Migrations\Upgrades\SqlCeDataUpgradeTest.cs" />