Files
Umbraco-CMS/src/Umbraco.Infrastructure/Migrations/MigrationBase.cs
Nikolaj Geisle eb31889be9 V12: Cherry pick unscoped migrations (#14086)
* New Backoffice: Refactor migrations to allow for unscoped migrations (#13654)

* Remove PostMigrations

These should be replaced with Notification usage

* Remove outer scope from Upgrader

* Remove unececary null check

* Add marker base class for migrations

* Enable scopeless migrations

* Remove unnecessary state check

The final state of the migration is no longer necessarily the final state of the plan.

* Extend ExecutedMigrationPlan

* Ensure that MigrationPlanExecutor.Execute always returns a result.

* Always save final state, regardless of errors

* Remove obsolete Execute

* Add Umbraco specific migration notification

* Publish notification after umbraco migration

* Throw the exception that failed a migration after publishing notification

* Handle notification publishing in DatabaseBuilder

* Fix tests

* Remember to complete scope

* Clean up MigrationPlanExecutor

* Run each package migration in a separate scope

* Add PartialMigrationsTests

* Add unhappy path test

* Fix bug shown by test

* Move PartialMigrationsTests into the correct folder

* Comment out refresh cache in data type migration

Need to add this back again as a notification handler or something.

* Start working on a notification test

* Allow migrations to request a cache rebuild

* Set RebuildCache from MigrateDataTypeConfigurations

* Clean MigrationPlanExecutor

* Add comment explaining the need to partial migration success

* Fix tests

* Allow overriding DefinePlan of UmbracoPlan

This is needed to test the DatabaseBuilder

* Fix notification test

* Don't throw exception to be immediately re-caught

* Assert that scopes notification are always published

* Ensure that scopes are created when requested

* Make test classes internal.

It doesn't really matter, but this way it doesn't show up in intellisense

* Add notification handler for clearing cookies

* Add CompatibilitySuppressions

* Rename Execute to ExecutePlan

We have to do this to be able to obsolete :(

* Update CompatibilitySuppressions

* Update src/Umbraco.Infrastructure/Migrations/MigrationPlanExecutor.cs

Co-authored-by: Bjarke Berg <mail@bergmania.dk>

Co-authored-by: Bjarke Berg <mail@bergmania.dk>

* generate compatability suppresion file

---------

Co-authored-by: Mole <nikolajlauridsen@protonmail.ch>
Co-authored-by: Bjarke Berg <mail@bergmania.dk>
Co-authored-by: Zeegaan <nge@umbraco.dk>
2023-04-13 12:23:44 +02:00

138 lines
4.3 KiB
C#

using Microsoft.Extensions.Logging;
using NPoco;
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Infrastructure.Migrations.Expressions.Alter;
using Umbraco.Cms.Infrastructure.Migrations.Expressions.Create;
using Umbraco.Cms.Infrastructure.Migrations.Expressions.Delete;
using Umbraco.Cms.Infrastructure.Migrations.Expressions.Execute;
using Umbraco.Cms.Infrastructure.Migrations.Expressions.Insert;
using Umbraco.Cms.Infrastructure.Migrations.Expressions.Rename;
using Umbraco.Cms.Infrastructure.Migrations.Expressions.Update;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Infrastructure.Persistence.SqlSyntax;
namespace Umbraco.Cms.Infrastructure.Migrations;
/// <summary>
/// Provides a base class to all migrations.
/// </summary>
public abstract partial class MigrationBase : IDiscoverable
{
/// <summary>
/// Initializes a new instance of the <see cref="MigrationBase" /> class.
/// </summary>
/// <param name="context">A migration context.</param>
protected MigrationBase(IMigrationContext context)
=> Context = context;
/// <summary>
/// Builds an Alter expression.
/// </summary>
public IAlterBuilder Alter => BeginBuild(new AlterBuilder(Context));
/// <summary>
/// Gets the migration context.
/// </summary>
protected IMigrationContext Context { get; }
/// <summary>
/// Gets the logger.
/// </summary>
protected ILogger Logger => Context.Logger;
/// <summary>
/// Gets the Sql syntax.
/// </summary>
protected ISqlSyntaxProvider SqlSyntax => Context.SqlContext.SqlSyntax;
/// <summary>
/// Gets the database instance.
/// </summary>
protected IUmbracoDatabase Database => Context.Database;
/// <summary>
/// Gets the database type.
/// </summary>
protected DatabaseType DatabaseType => Context.Database.DatabaseType;
/// <summary>
/// Builds a Create expression.
/// </summary>
public ICreateBuilder Create => BeginBuild(new CreateBuilder(Context));
/// <summary>
/// Builds a Delete expression.
/// </summary>
public IDeleteBuilder Delete => BeginBuild(new DeleteBuilder(Context));
/// <summary>
/// Builds an Execute expression.
/// </summary>
public IExecuteBuilder Execute => BeginBuild(new ExecuteBuilder(Context));
/// <summary>
/// Builds an Insert expression.
/// </summary>
public IInsertBuilder Insert => BeginBuild(new InsertBuilder(Context));
/// <summary>
/// Builds a Rename expression.
/// </summary>
public IRenameBuilder Rename => BeginBuild(new RenameBuilder(Context));
/// <summary>
/// Builds an Update expression.
/// </summary>
public IUpdateBuilder Update => BeginBuild(new UpdateBuilder(Context));
/// <summary>
/// If this is set to true, the published cache will be rebuild upon successful completion of the migration.
/// </summary>
public bool RebuildCache { get; set; }
/// <summary>
/// Runs the migration.
/// </summary>
public void Run()
{
Migrate();
// ensure there is no building expression
// ie we did not forget to .Do() an expression
if (Context.BuildingExpression)
{
throw new IncompleteMigrationExpressionException(
"The migration has run, but leaves an expression that has not run.");
}
}
/// <summary>
/// Creates a new Sql statement.
/// </summary>
protected Sql<ISqlContext> Sql() => Context.SqlContext.Sql();
/// <summary>
/// Creates a new Sql statement with arguments.
/// </summary>
protected Sql<ISqlContext> Sql(string sql, params object[] args) => Context.SqlContext.Sql(sql, args);
/// <summary>
/// Executes the migration.
/// </summary>
protected abstract void Migrate();
// ensures we are not already building,
// ie we did not forget to .Do() an expression
private protected T BeginBuild<T>(T builder)
{
if (Context.BuildingExpression)
{
throw new IncompleteMigrationExpressionException(
"Cannot create a new expression: the previous expression has not run.");
}
Context.BuildingExpression = true;
return builder;
}
}