Lesson Weekend

So far, we've manually created and updated our database using MySQLWorkbench. However, this approach isn't very effective for a large production application where many developers are collaborating together. Let's say we have many different teams that are working on different features in an application and we're working on a team that makes changes to the database. How can we convey the changes to other teams working on the application? We'd need to give everyone exact instructions on the changes we made, which could get tedious fast, especially if we're making a lot of changes.

It's very common for a database to change over time. In fact, in large projects, there may be hundreds or even thousands of changes to a database over the course of a project. It would be very tedious to make all those changes in MySQLWorkbench or another database tool.

Fortunately, Entity provides a solution for this. We can use migrations to create a new database or update an existing database based on our code. Migrations are a common feature of many frameworks, including Rails, which uses ActiveRecord.

A migration is simply a file that describes an update to the database. Each time the database is changed, a new migration is created. A large project could have thousands of migrations. In order to recreate the schema, a developer simply needs to run a command and all the migrations will run sequentially, updating the database to its current state. This can make our programs more flexible and significantly speed up the time it takes to make changes to our database.

We use a tool called dotnet ef to create migrations and update our database. We'll install this tool globally so that it is always available in all of our projects. Run the following command in your terminal:

$ dotnet tool install --global dotnet-ef --version 5.0.1

You can read more about the dotnet ef tool on the docs.

In the next lesson, we'll actually run our first migrations. Once all of the necessary setup is in place and we can successfully run dotnet build, we'll run a command in the root directory of the project, ToDoList, that looks like this:

$ dotnet ef migrations add Initial

If there is an error stating Unable to resolve project, this means the command wasn't run in the correct directory.

The command above will create a migration with the name Initial. Note that we can name it anything we want, but it's common for the first migration to contain the word Initial or something like it. Entity will automatically generate code for how the database should look based on the code that is in our models. This is called a Code First Migration because we will first write our models and then Entity will create a migration based on that code. If this is the first migration in a project, Entity will automatically generate a Migrations folder in the root directory.

For each migration, Entity will create three files in the Migrations directory:

[Timestamp]_Initial.cs
[Timestamp]_Initial.Designer.cs
MyContextModelSnapshot.cs

Note that [Timestamp] will vary based on when the migration was created and Initial will be different based on the name of the migration.

The second file is metadata that Entity needs while the third file is a snapshot of the database for Entity. We won't touch these files. The first file, though, can be edited. Here's an example migration file that we can edit (this is an example only - do not edit this file now):

using Microsoft.EntityFrameworkCore.Migrations;

namespace ToDoList.Migrations
{
    public partial class Initial : Migration
    {
        protected override void Up(MigrationBuilder migrationBuilder)
        {
            ...
        }

        protected override void Down(MigrationBuilder migrationBuilder)
        {
            ...
        }
    }
}

The migration contains two methods: Up and Down. In an ideal world, Entity will create a perfect scaffold of the migration for us based on our models. However, this isn't always the case. We can add further modifications to the Up method. The Down method provides instructions for reversing the migration if we decide to revert to the previous migration.

We will generally rely on Entity to take care of migration scaffolding for us, but it's important to be aware that these migrations can be fine-tuned further by modifying the migration file.

Once we have verified that the migration looks correct and made any necessary changes, we'll run the following command:

$ dotnet ef database update

This will automatically update our database to reflect the changes. There is no longer any need for us to directly modify the database in MySQLWorkbench!

Remember that migrations provide a record of how our database changes. If we make unwanted changes to our database, we should always use a new migration (or several migrations) to reverse the changes. This helps ensure that our data is safe and that the log of our migrations match the code in our migration files. It also makes it easy to make changes to a database in a large project where many developers are collaborating. We can push our migrations to Github and then another developer can pull our updates and run the migrations with a single command.

There is one exception to the rule above: let's say that we made a mistake with our most recent migration and we haven't pushed the changes to Github. We can use the following command to revert the migration:

$ dotnet ef migrations remove

For more information on migrations in EF Core, see the official documentation.

Lesson 3 of 14
Last updated March 14, 2022