Whether you are interested in upgrading Umbraco, migrating content, making changes to doctypes, updating packages, or need to edit data fields on a database level, migrations have you covered! Find out how to create and run migrations using Umbraco v10+.
What is a Migration?
Simply put: A migration is a snippet of code that can be executed at the start of your Umbraco application, that can be configured to run in a specific order, with access to the Umbraco database, meant to usually only be ran once. This can be particularly useful for when you are making changes outside of the code scope, that will also need to be updated on other environments of your Umbraco website (e.g. a development, staging and production environment), and you wish for those particular changes to be made on all environments only once.
Example one: You are a package developer, whose package makes use of a custom database table. While creating a new version of said package, you decide that a change to the table structure is necessary. You can include a Migration in your update, so that anyone that decides to update your package, will also automatically have the newly required table structure be created/altered for them!
Example two: You are upgrading from Umbraco v8 to Umbraco v10. You notice that a package you were using in v8 no longer exists for v10, yet your application relies on a custom data type added by said package. You find a package that works similar to the one you used to use, yet their data structures are incompatible. You can write a Migration to convert the data added by the v8 package to match the data structure of the v10 package, so that no data-loss occurs when upgrading!
Creating a Migration
To create and run our migration, we will start by creating a Composer & Component. If you are new to the concept of Composers, you can read more about it in this Blogpost, or on the Umbraco Docs. In our Component, we will need to inject the following four services that we will use to create our migration:
The RuntimeState is so that we can determine the required RuntimeState Level on which we wish our migration to run, and the other three are required for the MigrationPlan itself!
Let's take a look at the code snippet below for an example on how to setup a Migration Composer & Component:
For this example we will be looking at the use-case of migrating all the content on a specific document type from one property to another, so that we can get rid of the old property afterwards without losing any data!
The first thing we will do is check our runtimeState. Let's say we only wish to execute our migration when our Umbraco application is up-and-running.
Next up, we create a Migration Plan, and give it a name. Make sure this name is unique, as it will be used as the key in the stored keyvalue pair, that Umbraco will use to determine if your migration has previously ran or not!
After that, we will need to add a From and To state. If your migration doesn't depend on another migration, we can simply use an empty string as the From state, if not, use the value of an existing migration. For the To state, we will be creating a new class that will perform our Migration logic, and give it a value (in this case, "homepage-property-field"
And lastly, we will be creating a new instance of Upgrader (in the Umbraco.Cms.Infrastructure.Migrations.Upgrade namespace), to which we pass our Migration Plan using its constructor, after which we tell the upgrader to Excecute the plan!
Now let's take a look at the migration itself!
The first thing you'll notice is that our migration inherits from the MigrationBase class. This is required for our MigrationPlan to be able to execute our code. By inheriting from MigrationBase, we will be able to override the Migrate() method, that will be executed by the Upgrader. We will also need to inject the IMigrationContext through our migration's constructor, to pass along to the base class. We also make use of Dependency Injection over here to inject any service we may need in our migration! In this example, we will be using the ContentService to migrate data from one property to another on our existing content.
Let's take a look at this basic example. We have a website that has one or more Homepages. The homepages used to have a property with an alias of "oldProperty", a textstring. Our goal for this specific migration is to copy the values from oldProperty, to a new property called "newProperty", a textarea, and then clear the data of oldProperty. Once that's done, we'll save & publish our page so that our site reflects the new value(s)!
Now, when we run our application, we will notice the following additional Console output:
[19:25:06 INF] Starting 'HomepageMigration'...
[19:25:06 INF] At origin
[19:25:06 INF] Execute HomepageNewPropertyFieldMigration
[19:25:06 INF] Document Homepage (id=1057) has been published.
[19:25:06 INF] At homepage-property-field
[19:25:06 INF] Done (pending scope completion).
Which means our migration did its job! To confirm that Umbraco registered our migration correctly, we can take a look in the Database and search for the umbracoKeyValue table. In here, we will notice that the following row has been added, if all went well!
Umbraco.Core.Upgrader.State+HomepageMigration | homepage-property-field | 19:25:06.9533423
Meaning that the migration was registered correctly, and won't try to execute it another time. (If you do wish to execute your migration another time, you can remove this database entry and re-run your application!)
TIP: Looking to do something more complex that involves the Umbraco database? Through the MigrationBase base class you have access to a variety of database methods for creating, altering and deleting data & data structures!
Migrations can make your life a lot easier when working on updates, no matter if you are working on an Umbraco website, or developing a package. By creating a MigrationPlan, a migration to execute, and instantiating the Upgrader to execute said plan, you have full control over what happens with your data & data structures when upgrading/migrating! 🔥
If you have any further questions, feel free to contact me over at my socials available at the Contact page, and I'd love to hear about the amazing solutions you're able to come up with! 😄
Sources used in this article:
Umbraco Documentation, https://our.umbraco.com/documentation/Implementation/Composing/
Umbraco API Documentation, https://apidocs.umbraco.com/v9/csharp/api/Umbraco.Cms.Infrastructure.Migrations.html