diff --git a/src/Umbraco.Core/Notifications/MigrationPlanExecuted.cs b/src/Umbraco.Core/Notifications/MigrationPlanExecuted.cs
new file mode 100644
index 0000000000..9db7b0cf61
--- /dev/null
+++ b/src/Umbraco.Core/Notifications/MigrationPlanExecuted.cs
@@ -0,0 +1,19 @@
+namespace Umbraco.Cms.Core.Notifications
+{
+ ///
+ /// Published when a migration plan has been successfully executed.
+ ///
+ public class MigrationPlanExecuted : INotification
+ {
+ public MigrationPlanExecuted(string migrationPlanName, string initialState, string finalState)
+ {
+ MigrationPlanName = migrationPlanName;
+ InitialState = initialState;
+ FinalState = finalState;
+ }
+
+ public string MigrationPlanName { get; }
+ public string InitialState { get; }
+ public string FinalState { get; }
+ }
+}
diff --git a/src/Umbraco.Core/Notifications/RuntimeUnattendedUpgradeNotification.cs b/src/Umbraco.Core/Notifications/RuntimeUnattendedUpgradeNotification.cs
index 50c02cbad5..4d676f68ce 100644
--- a/src/Umbraco.Core/Notifications/RuntimeUnattendedUpgradeNotification.cs
+++ b/src/Umbraco.Core/Notifications/RuntimeUnattendedUpgradeNotification.cs
@@ -1,5 +1,6 @@
namespace Umbraco.Cms.Core.Notifications
{
+
///
/// Used to notify when the core runtime can do an unattended upgrade.
///
diff --git a/src/Umbraco.Core/Notifications/UnattendedPackageMigrationsExecutedNotification.cs b/src/Umbraco.Core/Notifications/UnattendedPackageMigrationsExecutedNotification.cs
new file mode 100644
index 0000000000..42768f6539
--- /dev/null
+++ b/src/Umbraco.Core/Notifications/UnattendedPackageMigrationsExecutedNotification.cs
@@ -0,0 +1,19 @@
+using System.Collections.Generic;
+
+namespace Umbraco.Cms.Core.Notifications
+{
+
+ ///
+ /// Published when unattended package migrations have been successfully executed
+ ///
+ public class UnattendedPackageMigrationsExecutedNotification : INotification
+ {
+ public UnattendedPackageMigrationsExecutedNotification(IReadOnlyList packageMigrations)
+ => PackageMigrations = packageMigrations;
+
+ ///
+ /// The list of package migration names that have been executed.
+ ///
+ public IReadOnlyList PackageMigrations { get; }
+ }
+}
diff --git a/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseUpgradeStep.cs b/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseUpgradeStep.cs
index 9e11cbd51f..3fe5493c37 100644
--- a/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseUpgradeStep.cs
+++ b/src/Umbraco.Infrastructure/Install/InstallSteps/DatabaseUpgradeStep.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
@@ -40,7 +40,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
_connectionStrings = connectionStrings.Value ?? throw new ArgumentNullException(nameof(connectionStrings));
}
- public override Task ExecuteAsync(object model)
+ public override async Task ExecuteAsync(object model)
{
var installSteps = InstallStatusTracker.GetStatus().ToArray();
var previousStep = installSteps.Single(x => x.Name == "DatabaseInstall");
@@ -53,7 +53,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
var plan = new UmbracoPlan(_umbracoVersion);
plan.AddPostMigration(); // needed when running installer (back-office)
- var result = _databaseBuilder.UpgradeSchemaAndData(plan);
+ var result = await _databaseBuilder.UpgradeSchemaAndDataAsync(plan);
if (result.Success == false)
{
@@ -61,7 +61,7 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
}
}
- return Task.FromResult(null);
+ return null;
}
public override bool RequiresExecution(object model)
diff --git a/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs b/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs
index a7ddf3378f..bf8aaa54ac 100644
--- a/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs
+++ b/src/Umbraco.Infrastructure/Install/InstallSteps/NewInstallStep.cs
@@ -137,6 +137,9 @@ namespace Umbraco.Cms.Infrastructure.Install.InstallSteps
{
var installState = InstallState.Unknown;
+ // TODO: we need to do a null check here since this could be entirely missing and we end up with a null ref
+ // exception in the installer.
+
var databaseSettings = _connectionStrings.UmbracoConnectionString;
var hasConnString = databaseSettings != null && _databaseBuilder.IsDatabaseConfigured;
diff --git a/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs b/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs
index 24cbce273f..34a9fc7bbc 100644
--- a/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs
+++ b/src/Umbraco.Infrastructure/Install/UnattendedUpgrader.cs
@@ -20,6 +20,10 @@ using Umbraco.Cms.Core;
namespace Umbraco.Cms.Infrastructure.Install
{
+ ///
+ /// Handles to execute the unattended Umbraco upgrader
+ /// or the unattended Package migrations runner.
+ ///
public class UnattendedUpgrader : INotificationAsyncHandler
{
private readonly IProfilingLogger _profilingLogger;
@@ -30,6 +34,7 @@ namespace Umbraco.Cms.Infrastructure.Install
private readonly IMigrationPlanExecutor _migrationPlanExecutor;
private readonly IScopeProvider _scopeProvider;
private readonly IKeyValueService _keyValueService;
+ private readonly IEventAggregator _eventAggregator;
public UnattendedUpgrader(
IProfilingLogger profilingLogger,
@@ -39,7 +44,8 @@ namespace Umbraco.Cms.Infrastructure.Install
PackageMigrationPlanCollection packageMigrationPlans,
IMigrationPlanExecutor migrationPlanExecutor,
IScopeProvider scopeProvider,
- IKeyValueService keyValueService)
+ IKeyValueService keyValueService,
+ IEventAggregator eventAggregator)
{
_profilingLogger = profilingLogger ?? throw new ArgumentNullException(nameof(profilingLogger));
_umbracoVersion = umbracoVersion ?? throw new ArgumentNullException(nameof(umbracoVersion));
@@ -49,9 +55,10 @@ namespace Umbraco.Cms.Infrastructure.Install
_migrationPlanExecutor = migrationPlanExecutor;
_scopeProvider = scopeProvider;
_keyValueService = keyValueService;
+ _eventAggregator = eventAggregator;
}
- public Task HandleAsync(RuntimeUnattendedUpgradeNotification notification, CancellationToken cancellationToken)
+ public async Task HandleAsync(RuntimeUnattendedUpgradeNotification notification, CancellationToken cancellationToken)
{
if (_runtimeState.RunUnattendedBootLogic())
{
@@ -64,12 +71,11 @@ namespace Umbraco.Cms.Infrastructure.Install
"Starting unattended upgrade.",
"Unattended upgrade completed."))
{
- DatabaseBuilder.Result result = _databaseBuilder.UpgradeSchemaAndData(plan);
+ DatabaseBuilder.Result result = await _databaseBuilder.UpgradeSchemaAndDataAsync(plan);
if (result.Success == false)
{
var innerException = new UnattendedInstallException("An error occurred while running the unattended upgrade.\n" + result.Message);
_runtimeState.Configure(Core.RuntimeLevel.BootFailed, Core.RuntimeLevelReason.BootFailedOnException, innerException);
- return Task.CompletedTask;
}
notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult.CoreUpgradeComplete;
@@ -83,7 +89,7 @@ namespace Umbraco.Cms.Infrastructure.Install
{
throw new InvalidOperationException($"The required key {RuntimeState.PendingPacakgeMigrationsStateKey} does not exist in startup state");
}
-
+
if (pendingMigrations.Count == 0)
{
throw new InvalidOperationException("No pending migrations found but the runtime level reason is " + Core.RuntimeLevelReason.UpgradePackageMigrations);
@@ -92,26 +98,38 @@ namespace Umbraco.Cms.Infrastructure.Install
var exceptions = new List();
var packageMigrationsPlans = _packageMigrationPlans.ToDictionary(x => x.Name);
- foreach (var migrationName in pendingMigrations)
+ // Create an explicit scope around all package migrations so they are
+ // all executed in a single transaction.
+ using (IScope scope = _scopeProvider.CreateScope(autoComplete: true))
{
- if (!packageMigrationsPlans.TryGetValue(migrationName, out PackageMigrationPlan plan))
+ // We want to suppress scope (service, etc...) notifications during a migration plan
+ // execution. This is because if a package that doesn't have their migration plan
+ // executed is listening to service notifications to perform some persistence logic,
+ // that packages notification handlers may explode because that package isn't fully installed yet.
+ using (scope.Notifications.Suppress())
{
- throw new InvalidOperationException("Cannot find package migration plan " + migrationName);
- }
-
- using (_profilingLogger.TraceDuration(
- "Starting unattended package migration for " + migrationName,
- "Unattended upgrade completed for " + migrationName))
- {
- var upgrader = new Upgrader(plan);
-
- try
+ foreach (var migrationName in pendingMigrations)
{
- upgrader.Execute(_migrationPlanExecutor, _scopeProvider, _keyValueService);
- }
- catch (Exception ex)
- {
- exceptions.Add(new UnattendedInstallException("Unattended package migration failed for " + migrationName, ex));
+ if (!packageMigrationsPlans.TryGetValue(migrationName, out PackageMigrationPlan plan))
+ {
+ throw new InvalidOperationException("Cannot find package migration plan " + migrationName);
+ }
+
+ using (_profilingLogger.TraceDuration(
+ "Starting unattended package migration for " + migrationName,
+ "Unattended upgrade completed for " + migrationName))
+ {
+ var upgrader = new Upgrader(plan);
+
+ try
+ {
+ await upgrader.ExecuteAsync(_migrationPlanExecutor, _scopeProvider, _keyValueService);
+ }
+ catch (Exception ex)
+ {
+ exceptions.Add(new UnattendedInstallException("Unattended package migration failed for " + migrationName, ex));
+ }
+ }
}
}
}
@@ -123,6 +141,9 @@ namespace Umbraco.Cms.Infrastructure.Install
}
else
{
+ var packageMigrationsExecutedNotification = new UnattendedPackageMigrationsExecutedNotification(pendingMigrations);
+ await _eventAggregator.PublishAsync(packageMigrationsExecutedNotification);
+
notification.UnattendedUpgradeResult = RuntimeUnattendedUpgradeNotification.UpgradeResult.PackageMigrationComplete;
}
}
@@ -131,11 +152,10 @@ namespace Umbraco.Cms.Infrastructure.Install
throw new InvalidOperationException("Invalid reason " + _runtimeState.Reason);
}
}
- return Task.CompletedTask;
}
private void SetRuntimeErrors(List exception)
- {
+ {
Exception innerException;
if (exception.Count == 1)
{
diff --git a/src/Umbraco.Infrastructure/Migrations/IMigrationPlanExecutor.cs b/src/Umbraco.Infrastructure/Migrations/IMigrationPlanExecutor.cs
index 4610e02d60..47de12a3f2 100644
--- a/src/Umbraco.Infrastructure/Migrations/IMigrationPlanExecutor.cs
+++ b/src/Umbraco.Infrastructure/Migrations/IMigrationPlanExecutor.cs
@@ -1,9 +1,10 @@
+using System.Threading.Tasks;
using Umbraco.Cms.Infrastructure.Migrations;
namespace Umbraco.Cms.Core.Migrations
{
public interface IMigrationPlanExecutor
{
- string Execute(MigrationPlan plan, string fromState);
+ Task ExecuteAsync(MigrationPlan plan, string fromState);
}
}
diff --git a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
index 691400121f..6110279442 100644
--- a/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Install/DatabaseBuilder.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Configuration;
@@ -412,7 +413,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
/// configured and it is possible to connect to the database.
/// Runs whichever migrations need to run.
///
- public Result UpgradeSchemaAndData(UmbracoPlan plan)
+ public async Task UpgradeSchemaAndDataAsync(UmbracoPlan plan)
{
try
{
@@ -426,7 +427,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Install
// upgrade
var upgrader = new Upgrader(plan);
- upgrader.Execute(_migrationPlanExecutor, _scopeProvider, _keyValueService);
+ await upgrader.ExecuteAsync(_migrationPlanExecutor, _scopeProvider, _keyValueService);
var message = "Upgrade completed!
";
diff --git a/src/Umbraco.Infrastructure/Migrations/MigrationPlan.cs b/src/Umbraco.Infrastructure/Migrations/MigrationPlan.cs
index 9d0f110a74..bdb5aeb780 100644
--- a/src/Umbraco.Infrastructure/Migrations/MigrationPlan.cs
+++ b/src/Umbraco.Infrastructure/Migrations/MigrationPlan.cs
@@ -1,8 +1,6 @@
-using System;
+using System;
using System.Collections.Generic;
using System.Linq;
-using Microsoft.Extensions.Logging;
-using Umbraco.Cms.Core.Scoping;
using Umbraco.Extensions;
using Type = System.Type;
@@ -217,6 +215,11 @@ namespace Umbraco.Cms.Infrastructure.Migrations
public virtual MigrationPlan AddPostMigration()
where TMigration : MigrationBase
{
+ // TODO: Post migrations are obsolete/irrelevant. Notifications should be used instead.
+ // The only place we use this is to clear cookies in the installer which could be done
+ // via notification. Then we can clean up all the code related to post migrations which is
+ // not insignificant.
+
_postMigrationTypes.Add(typeof(TMigration));
return this;
}
diff --git a/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs b/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs
index 51fc613c21..b4a7616bc8 100644
--- a/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs
+++ b/src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs
@@ -1,8 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
+using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Migrations;
+using Umbraco.Cms.Core.Notifications;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Extensions;
using Type = System.Type;
@@ -14,13 +17,19 @@ namespace Umbraco.Cms.Infrastructure.Migrations
private readonly IScopeProvider _scopeProvider;
private readonly ILoggerFactory _loggerFactory;
private readonly IMigrationBuilder _migrationBuilder;
+ private readonly IEventAggregator _eventAggregator;
private readonly ILogger _logger;
- public MigrationPlanExecutor(IScopeProvider scopeProvider, ILoggerFactory loggerFactory, IMigrationBuilder migrationBuilder)
+ public MigrationPlanExecutor(
+ IScopeProvider scopeProvider,
+ ILoggerFactory loggerFactory,
+ IMigrationBuilder migrationBuilder,
+ IEventAggregator eventAggregator)
{
_scopeProvider = scopeProvider;
_loggerFactory = loggerFactory;
_migrationBuilder = migrationBuilder;
+ _eventAggregator = eventAggregator;
_logger = _loggerFactory.CreateLogger();
}
@@ -34,58 +43,67 @@ namespace Umbraco.Cms.Infrastructure.Migrations
///
/// The final state.
/// The plan executes within the scope, which must then be completed.
- public string Execute(MigrationPlan plan, string fromState)
+ public async Task ExecuteAsync(MigrationPlan plan, string fromState)
{
plan.Validate();
_logger.LogInformation("Starting '{MigrationName}'...", plan.Name);
- var origState = fromState ?? string.Empty;
+ fromState ??= string.Empty;
+ var nextState = fromState;
- _logger.LogInformation("At {OrigState}", string.IsNullOrWhiteSpace(origState) ? "origin" : origState);
+ _logger.LogInformation("At {OrigState}", string.IsNullOrWhiteSpace(nextState) ? "origin" : nextState);
- if (!plan.Transitions.TryGetValue(origState, out MigrationPlan.Transition transition))
+ if (!plan.Transitions.TryGetValue(nextState, out MigrationPlan.Transition transition))
{
- plan.ThrowOnUnknownInitialState(origState);
+ plan.ThrowOnUnknownInitialState(nextState);
}
using (IScope scope = _scopeProvider.CreateScope(autoComplete: true))
{
- var context = new MigrationContext(plan, scope.Database, _loggerFactory.CreateLogger());
-
- while (transition != null)
+ // We want to suppress scope (service, etc...) notifications during a migration plan
+ // execution. This is because if a package that doesn't have their migration plan
+ // executed is listening to service notifications to perform some persistence logic,
+ // that packages notification handlers may explode because that package isn't fully installed yet.
+ using (scope.Notifications.Suppress())
{
- _logger.LogInformation("Execute {MigrationType}", transition.MigrationType.Name);
+ var context = new MigrationContext(plan, scope.Database, _loggerFactory.CreateLogger());
- var migration = _migrationBuilder.Build(transition.MigrationType, context);
- migration.Run();
-
- var nextState = transition.TargetState;
- origState = nextState;
-
- _logger.LogInformation("At {OrigState}", origState);
-
- // throw a raw exception here: this should never happen as the plan has
- // been validated - this is just a paranoid safety test
- if (!plan.Transitions.TryGetValue(origState, out transition))
+ while (transition != null)
{
- throw new InvalidOperationException($"Unknown state \"{origState}\".");
+ _logger.LogInformation("Execute {MigrationType}", transition.MigrationType.Name);
+
+ var migration = _migrationBuilder.Build(transition.MigrationType, context);
+ migration.Run();
+
+ nextState = transition.TargetState;
+
+ _logger.LogInformation("At {OrigState}", nextState);
+
+ // throw a raw exception here: this should never happen as the plan has
+ // been validated - this is just a paranoid safety test
+ if (!plan.Transitions.TryGetValue(nextState, out transition))
+ {
+ throw new InvalidOperationException($"Unknown state \"{nextState}\".");
+ }
+ }
+
+ // prepare and de-duplicate post-migrations, only keeping the 1st occurence
+ var temp = new HashSet();
+ var postMigrationTypes = context.PostMigrations
+ .Where(x => !temp.Contains(x))
+ .Select(x => { temp.Add(x); return x; });
+
+ // run post-migrations
+ foreach (var postMigrationType in postMigrationTypes)
+ {
+ _logger.LogInformation($"PostMigration: {postMigrationType.FullName}.");
+ var postMigration = _migrationBuilder.Build(postMigrationType, context);
+ postMigration.Run();
}
}
- // prepare and de-duplicate post-migrations, only keeping the 1st occurence
- var temp = new HashSet();
- var postMigrationTypes = context.PostMigrations
- .Where(x => !temp.Contains(x))
- .Select(x => { temp.Add(x); return x; });
-
- // run post-migrations
- foreach (var postMigrationType in postMigrationTypes)
- {
- _logger.LogInformation($"PostMigration: {postMigrationType.FullName}.");
- var postMigration = _migrationBuilder.Build(postMigrationType, context);
- postMigration.Run();
- }
+ await _eventAggregator.PublishAsync(new MigrationPlanExecuted(plan.Name, fromState, nextState));
}
_logger.LogInformation("Done (pending scope completion).");
@@ -93,12 +111,12 @@ namespace Umbraco.Cms.Infrastructure.Migrations
// safety check - again, this should never happen as the plan has been validated,
// and this is just a paranoid safety test
var finalState = plan.FinalState;
- if (origState != finalState)
+ if (nextState != finalState)
{
- throw new InvalidOperationException($"Internal error, reached state {origState} which is not final state {finalState}");
+ throw new InvalidOperationException($"Internal error, reached state {nextState} which is not final state {finalState}");
}
- return origState;
+ return nextState;
}
}
}
diff --git a/src/Umbraco.Infrastructure/Migrations/Upgrade/Upgrader.cs b/src/Umbraco.Infrastructure/Migrations/Upgrade/Upgrader.cs
index fc0e01c3d9..44b9f3c708 100644
--- a/src/Umbraco.Infrastructure/Migrations/Upgrade/Upgrader.cs
+++ b/src/Umbraco.Infrastructure/Migrations/Upgrade/Upgrader.cs
@@ -1,4 +1,5 @@
using System;
+using System.Threading.Tasks;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Migrations;
using Umbraco.Cms.Core.Scoping;
@@ -7,7 +8,7 @@ using Umbraco.Cms.Core.Services;
namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade
{
///
- /// Represents an upgrader.
+ /// Used to run a
///
public class Upgrader
{
@@ -36,13 +37,13 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade
///
/// A scope provider.
/// A key-value service.
- public void Execute(IMigrationPlanExecutor migrationPlanExecutor, IScopeProvider scopeProvider, IKeyValueService keyValueService)
+ public async Task ExecuteAsync(IMigrationPlanExecutor migrationPlanExecutor, IScopeProvider scopeProvider, IKeyValueService keyValueService)
{
if (scopeProvider == null) throw new ArgumentNullException(nameof(scopeProvider));
if (keyValueService == null) throw new ArgumentNullException(nameof(keyValueService));
- using (var scope = scopeProvider.CreateScope())
- {
+ using (IScope scope = scopeProvider.CreateScope())
+ {
// read current state
var currentState = keyValueService.GetValue(StateValueKey);
var forceState = false;
@@ -54,7 +55,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade
}
// execute plan
- var state = migrationPlanExecutor.Execute(Plan, currentState);
+ var state = await migrationPlanExecutor.ExecuteAsync(Plan, currentState);
if (string.IsNullOrWhiteSpace(state))
{
throw new Exception("Plan execution returned an invalid null or empty state.");
@@ -69,7 +70,7 @@ namespace Umbraco.Cms.Infrastructure.Migrations.Upgrade
{
keyValueService.SetValue(StateValueKey, currentState, state);
}
-
+
scope.Complete();
}
}
diff --git a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Migrations/AdvancedMigrationTests.cs b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Migrations/AdvancedMigrationTests.cs
index b41e4dce49..0de597d61f 100644
--- a/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Migrations/AdvancedMigrationTests.cs
+++ b/src/Umbraco.Tests.Integration/Umbraco.Infrastructure/Migrations/AdvancedMigrationTests.cs
@@ -3,6 +3,7 @@
using System;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Moq;
@@ -31,7 +32,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Migrations
private IMigrationPlanExecutor MigrationPlanExecutor => GetRequiredService();
[Test]
- public void CreateTableOfTDto()
+ public async Task CreateTableOfTDto()
{
IMigrationBuilder builder = Mock.Of();
Mock.Get(builder)
@@ -53,7 +54,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Migrations
.From(string.Empty)
.To("done"));
- upgrader.Execute(MigrationPlanExecutor, ScopeProvider, Mock.Of());
+ await upgrader.ExecuteAsync(MigrationPlanExecutor, ScopeProvider, Mock.Of());
var helper = new DatabaseSchemaCreator(scope.Database, LoggerFactory.CreateLogger(), LoggerFactory, UmbracoVersion, EventAggregator);
bool exists = helper.TableExists("umbracoUser");
@@ -64,7 +65,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Migrations
}
[Test]
- public void DeleteKeysAndIndexesOfTDto()
+ public async Task DeleteKeysAndIndexesOfTDto()
{
IMigrationBuilder builder = Mock.Of();
Mock.Get(builder)
@@ -90,13 +91,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Migrations
.To("a")
.To("done"));
- upgrader.Execute(MigrationPlanExecutor, ScopeProvider, Mock.Of());
+ await upgrader.ExecuteAsync(MigrationPlanExecutor, ScopeProvider, Mock.Of());
scope.Complete();
}
}
[Test]
- public void CreateKeysAndIndexesOfTDto()
+ public async Task CreateKeysAndIndexesOfTDto()
{
IMigrationBuilder builder = Mock.Of();
Mock.Get(builder)
@@ -125,13 +126,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Migrations
.To("b")
.To("done"));
- upgrader.Execute(MigrationPlanExecutor, ScopeProvider, Mock.Of());
+ await upgrader.ExecuteAsync(MigrationPlanExecutor, ScopeProvider, Mock.Of());
scope.Complete();
}
}
[Test]
- public void CreateKeysAndIndexes()
+ public async Task CreateKeysAndIndexes()
{
IMigrationBuilder builder = Mock.Of();
Mock.Get(builder)
@@ -160,13 +161,13 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Migrations
.To("b")
.To("done"));
- upgrader.Execute(MigrationPlanExecutor, ScopeProvider, Mock.Of());
+ await upgrader.ExecuteAsync(MigrationPlanExecutor, ScopeProvider, Mock.Of());
scope.Complete();
}
}
[Test]
- public void CreateColumn()
+ public async Task CreateColumn()
{
IMigrationBuilder builder = Mock.Of();
Mock.Get(builder)
@@ -192,7 +193,7 @@ namespace Umbraco.Cms.Tests.Integration.Umbraco.Infrastructure.Migrations
.To("a")
.To("done"));
- upgrader.Execute(MigrationPlanExecutor, ScopeProvider, Mock.Of());
+ await upgrader.ExecuteAsync(MigrationPlanExecutor, ScopeProvider, Mock.Of());
scope.Complete();
}
}
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/MigrationPlanTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/MigrationPlanTests.cs
index 26616d9d69..8ee093372e 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/MigrationPlanTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/MigrationPlanTests.cs
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -11,6 +12,7 @@ using Moq;
using NPoco;
using NUnit.Framework;
using Umbraco.Cms.Core.Configuration.Models;
+using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Infrastructure.Migrations;
@@ -27,12 +29,12 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations
public class MigrationPlanTests
{
[Test]
- public void CanExecute()
+ public async Task CanExecute()
{
NullLoggerFactory loggerFactory = NullLoggerFactory.Instance;
var database = new TestDatabase();
- IScope scope = Mock.Of();
+ IScope scope = Mock.Of(x => x.Notifications == Mock.Of());
Mock.Get(scope)
.Setup(x => x.Database)
.Returns(database);
@@ -56,7 +58,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations
}
});
- var executor = new MigrationPlanExecutor(scopeProvider, loggerFactory, migrationBuilder);
+ var executor = new MigrationPlanExecutor(scopeProvider, loggerFactory, migrationBuilder, Mock.Of());
MigrationPlan plan = new MigrationPlan("default")
.From(string.Empty)
@@ -74,7 +76,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations
var sourceState = kvs.GetValue("Umbraco.Tests.MigrationPlan") ?? string.Empty;
// execute plan
- state = executor.Execute(plan, sourceState);
+ state = await executor.ExecuteAsync(plan, sourceState);
// save new state
kvs.SetValue("Umbraco.Tests.MigrationPlan", sourceState, state);
diff --git a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/PostMigrationTests.cs b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/PostMigrationTests.cs
index 2b6ff721eb..805473cd7f 100644
--- a/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/PostMigrationTests.cs
+++ b/src/Umbraco.Tests.UnitTests/Umbraco.Infrastructure/Migrations/PostMigrationTests.cs
@@ -1,7 +1,8 @@
-// Copyright (c) Umbraco.
+// Copyright (c) Umbraco.
// See LICENSE for more details.
using System;
+using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
@@ -9,6 +10,7 @@ using Moq;
using NPoco;
using NUnit.Framework;
using Umbraco.Cms.Core.Configuration.Models;
+using Umbraco.Cms.Core.Events;
using Umbraco.Cms.Core.Migrations;
using Umbraco.Cms.Core.Scoping;
using Umbraco.Cms.Core.Services;
@@ -25,10 +27,10 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations
{
private static readonly ILoggerFactory s_loggerFactory = NullLoggerFactory.Instance;
private IMigrationPlanExecutor GetMigrationPlanExecutor(IScopeProvider scopeProvider, IMigrationBuilder builder)
- => new MigrationPlanExecutor(scopeProvider, s_loggerFactory, builder);
+ => new MigrationPlanExecutor(scopeProvider, s_loggerFactory, builder, Mock.Of());
[Test]
- public void ExecutesPlanPostMigration()
+ public async Task ExecutesPlanPostMigration()
{
IMigrationBuilder builder = Mock.Of();
Mock.Get(builder)
@@ -47,7 +49,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations
});
var database = new TestDatabase();
- IScope scope = Mock.Of();
+ IScope scope = Mock.Of(x => x.Notifications == Mock.Of());
Mock.Get(scope)
.Setup(x => x.Database)
.Returns(database);
@@ -66,7 +68,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations
var upgrader = new Upgrader(plan);
IMigrationPlanExecutor executor = GetMigrationPlanExecutor(scopeProvider, builder);
- upgrader.Execute(
+ await upgrader.ExecuteAsync(
executor,
scopeProvider,
Mock.Of());
@@ -75,7 +77,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations
}
[Test]
- public void MigrationCanAddPostMigration()
+ public async Task MigrationCanAddPostMigration()
{
IMigrationBuilder builder = Mock.Of();
Mock.Get(builder)
@@ -96,7 +98,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations
});
var database = new TestDatabase();
- IScope scope = Mock.Of();
+ IScope scope = Mock.Of(x => x.Notifications == Mock.Of());
Mock.Get(scope)
.Setup(x => x.Database)
.Returns(database);
@@ -117,7 +119,7 @@ namespace Umbraco.Cms.Tests.UnitTests.Umbraco.Infrastructure.Migrations
var upgrader = new Upgrader(plan);
IMigrationPlanExecutor executor = GetMigrationPlanExecutor(scopeProvider, builder);
- upgrader.Execute(
+ await upgrader.ExecuteAsync(
executor,
scopeProvider,
Mock.Of());
diff --git a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
index dca3a320ec..2e5bb9c93d 100644
--- a/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
+++ b/src/Umbraco.Web.BackOffice/Controllers/PackageController.cs
@@ -22,6 +22,7 @@ using Umbraco.Cms.Core.Migrations;
using Umbraco.Cms.Core.Scoping;
using Microsoft.Extensions.Logging;
using System.Numerics;
+using System.Threading.Tasks;
namespace Umbraco.Cms.Web.BackOffice.Controllers
{
@@ -117,7 +118,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
}
[HttpPost]
- public ActionResult> RunMigrations([FromQuery]string packageName)
+ public async Task>> RunMigrations([FromQuery]string packageName)
{
IReadOnlyDictionary keyValues = _keyValueService.FindByKeyPrefix(Constants.Conventions.Migrations.KeyValuePrefix);
IReadOnlyList pendingMigrations = _pendingPackageMigrations.GetPendingPackageMigrations(keyValues);
@@ -129,7 +130,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
try
{
- upgrader.Execute(_migrationPlanExecutor, _scopeProvider, _keyValueService);
+ await upgrader.ExecuteAsync(_migrationPlanExecutor, _scopeProvider, _keyValueService);
}
catch (Exception ex)
{