Files
Umbraco-CMS/src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
Shannon 3df2d03fc8 Merge remote-tracking branch 'origin/6.2.0' into 7.1.2
Conflicts:
	build/Build.bat
	build/NuSpecs/UmbracoCms.Core.nuspec
	build/NuSpecs/build/UmbracoCms.targets
	src/Umbraco.Core/Models/IPublishedContentProperty.cs
	src/Umbraco.Core/Models/PublishedContent/IPublishedContentExtended.cs
	src/Umbraco.Core/Models/PublishedContent/PublishedContentExtended.cs
	src/Umbraco.Core/Models/PublishedContent/PublishedContentWrapped.cs
	src/Umbraco.Core/Models/PublishedContent/PublishedPropertyBase.cs
	src/Umbraco.Core/Models/PublishedContent/PublishedPropertyType.cs
	src/Umbraco.Core/Models/Template.cs
	src/Umbraco.Core/Persistence/Migrations/MigrationRunner.cs
	src/Umbraco.Core/Persistence/Repositories/MemberGroupRepository.cs
	src/Umbraco.Core/Persistence/Repositories/MemberRepository.cs
	src/Umbraco.Core/Persistence/Repositories/MemberTypeRepository.cs
	src/Umbraco.Core/Persistence/Repositories/RecycleBinRepository.cs
	src/Umbraco.Core/Services/PackagingService.cs
	src/Umbraco.Tests/PublishedContent/PublishedContentMoreTests.cs
	src/Umbraco.Tests/PublishedContent/PublishedContentTestElements.cs
	src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedFragment.cs
	src/Umbraco.Web/PublishedCache/XmlPublishedCache/PublishedMediaCache.cs
	src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs
	src/Umbraco.Web/PublishedContentPropertyExtension.cs
	src/Umbraco.Web/Search/ExamineEvents.cs
	src/Umbraco.Web/UmbracoHelper.cs
	src/Umbraco.Web/umbraco.presentation/CompatibilityHelper.cs
	src/Umbraco.Web/umbraco.presentation/macro.cs
	src/umbraco.cms/businesslogic/template/Template.cs
2014-05-05 12:49:06 +10:00

207 lines
9.8 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Umbraco.Core.Events;
using Umbraco.Core.Logging;
using Umbraco.Core.Persistence.Migrations.Syntax.IfDatabase;
namespace Umbraco.Core.Persistence.Migrations
{
/// <summary>
/// Represents the Migration Runner, which is used to apply migrations to
/// the umbraco database.
/// </summary>
public class MigrationRunner
{
private readonly Version _currentVersion;
private readonly Version _targetVersion;
private readonly string _productName;
public MigrationRunner(Version currentVersion, Version targetVersion, string productName)
{
_currentVersion = currentVersion;
_targetVersion = targetVersion;
_productName = productName;
}
/// <summary>
/// Executes the migrations against the database.
/// </summary>
/// <param name="database">The PetaPoco Database, which the migrations will be run against</param>
/// <param name="isUpgrade">Boolean indicating whether this is an upgrade or downgrade</param>
/// <returns><c>True</c> if migrations were applied, otherwise <c>False</c></returns>
public bool Execute(Database database, bool isUpgrade = true)
{
return Execute(database, database.GetDatabaseProvider(), isUpgrade);
}
/// <summary>
/// Executes the migrations against the database.
/// </summary>
/// <param name="database">The PetaPoco Database, which the migrations will be run against</param>
/// <param name="databaseProvider"></param>
/// <param name="isUpgrade">Boolean indicating whether this is an upgrade or downgrade</param>
/// <returns><c>True</c> if migrations were applied, otherwise <c>False</c></returns>
public bool Execute(Database database, DatabaseProviders databaseProvider, bool isUpgrade = true)
{
LogHelper.Info<MigrationRunner>("Initializing database migrations");
var foundMigrations = MigrationResolver.Current.Migrations.ToArray();
//filter all non-schema migrations
var migrations = isUpgrade
? OrderedUpgradeMigrations(foundMigrations).ToList()
: OrderedDowngradeMigrations(foundMigrations).ToList();
//SD: Why do we want this?
if (Migrating.IsRaisedEventCancelled(new MigrationEventArgs(migrations, _currentVersion, _targetVersion, true), this))
return false;
//Loop through migrations to generate sql
var migrationContext = InitializeMigrations(migrations, database, databaseProvider, isUpgrade);
try
{
ExecuteMigrations(migrationContext, database);
}
catch (Exception ex)
{
//if this fails then the transaction will be rolled back, BUT if we are using MySql this is not the case,
//since it does not support schema changes in a transaction, see: http://dev.mysql.com/doc/refman/5.0/en/implicit-commit.html
//so in that case we have to downgrade
if (databaseProvider == DatabaseProviders.MySql)
{
throw new DataLossException(
"An error occurred running a schema migration but the changes could not be rolled back. Error: " + ex.Message + ". In some cases, it may be required that the database be restored to it's original state before running this upgrade process again.",
ex);
}
//continue throwing the exception
throw;
}
Migrated.RaiseEvent(new MigrationEventArgs(migrations, migrationContext, _currentVersion, _targetVersion, false), this);
return true;
}
private void ExecuteMigrations(IMigrationContext context, Database database)
{
//Transactional execution of the sql that was generated from the found migrations
using (var transaction = database.GetTransaction())
{
int i = 1;
foreach (var expression in context.Expressions)
{
var sql = expression.Process(database);
if (string.IsNullOrEmpty(sql))
{
i++;
continue;
}
LogHelper.Info<MigrationRunner>("Executing sql statement " + i + ": " + sql);
database.Execute(sql);
i++;
}
transaction.Complete();
}
}
internal MigrationContext InitializeMigrations(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;
}
/// <summary>
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
/// </summary>
/// <param name="foundMigrations"></param>
/// <returns></returns>
internal IEnumerable<IMigration> OrderedUpgradeMigrations(IEnumerable<IMigration> foundMigrations)
{
var migrations = (from migration in foundMigrations
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _currentVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName &&
//filter if the migration specifies a minimum current version for which to execute
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder ascending
select migration).Distinct();
return migrations;
}
/// <summary>
/// Filters and orders migrations based on the migrations listed and the currently configured version and the target installation version
/// </summary>
/// <param name="foundMigrations"></param>
/// <returns></returns>
public IEnumerable<IMigration> OrderedDowngradeMigrations(IEnumerable<IMigration> foundMigrations)
{
var migrations = (from migration in foundMigrations
let migrationAttributes = migration.GetType().GetCustomAttributes<MigrationAttribute>(false)
from migrationAttribute in migrationAttributes
where migrationAttribute != null
where
migrationAttribute.TargetVersion > _currentVersion &&
migrationAttribute.TargetVersion <= _targetVersion &&
migrationAttribute.ProductName == _productName &&
//filter if the migration specifies a minimum current version for which to execute
(migrationAttribute.MinimumCurrentVersion == null || _currentVersion >= migrationAttribute.MinimumCurrentVersion)
orderby migrationAttribute.TargetVersion, migrationAttribute.SortOrder descending
select migration).Distinct();
return migrations;
}
/// <summary>
/// Occurs before Migration
/// </summary>
public static event TypedEventHandler<MigrationRunner, MigrationEventArgs> Migrating;
/// <summary>
/// Occurs after Migration
/// </summary>
public static event TypedEventHandler<MigrationRunner, MigrationEventArgs> Migrated;
}
}