In this recipe we will use Code First code Migrations to evolve our model and update the database. You might be asking yourself why this is so important. Isn't this just another way of doing something we already knew how to do?
Not so! It solves a very important problem in a simple and efficient way. Haven't we all at some time had the problem of deploying software and keeping the database up-to-date? Well, this solves this exact problem. And in a very easy and simple way, I must add.
How? Keep reading.
In order to use this recipe, you should have Visual Studio 2012 installed. It would be good to have implemented the previous recipe and worked on the resulting solution.
Using this recipe we will implement Code First Migrations and show how to evolve our model and revert it to a previous version.
First, open Visual Studio 2012 and open the previous project. We recommend copying it to a new folder so if anything goes wrong (you all know Murphy is always present) we can start a new one.
Change the project name to EFCodeFirstMigrations
.
We will start by enabling migrations. To do this, we will open the Package Manager Console option and type the Enable-Migrations
command into it, pressing Return to execute it. We will observe that a Migrations
folder has been created in the project with two source files, one for the initial creation of the database and the other for the configuration (configuration.cs
).
We will open the configuration.cs
file and change the AutomaticMigrationsEnabled
property's value to true
using the following line of code:
AutomaticMigrationsEnabled = true
Note that we could have enabled Automatic Migrations with this command:
Enable-Migrations – EnableAutomaticMigrations
Let's add a minor change to our BookModel
class to see how this works out. We will add a Boolean property named IsOnSale
as shown in the following code line:
public boolIsOnSale {get; set;}
Apply the pending changes from our code to the database with the command Update-Database
that we will type into and execute with the Package Manager Console. If we want more details on what is happening, we can also execute this command with the –Verbose
flag as shown in the following code line:
Update-Database –Verbose
It would be good to validate that our database has been properly generated, so we should open our Server Explorer panel and refresh it to reflect the changes.
Next, we will comment the added property and execute the Update-Database
command again. It will not be executed because it would result in data loss.
We will uncomment the IsOnSale
property and add a BookRating
integer property to the BookModel
class.
For medium to advanced scenarios for which we want more control over what happens, we have Code Migrations. We will enable it by going to our configuration.cs
file and setting the AutomaticMigrationsEnabled
property to false
.
Then we will go to our Package Manager Console and execute the Add-Migration
command, followed by the name we want to give to this code migration. We will use the name BookRating
as it describes very well what this migration will do.
Now a new file has appeared in our Migrations
folder, named BookRating.cs
, prefixed with a timestamp that will help us with the ordering. If we open it, we will clearly see and understand how it works:
public partial class BookRating : DbMigration { public override void Up() { AddColumn("dbo.BookModels", "BookRating", c =>c.Int(nullable: false)); } public override void Down() { DropColumn("dbo.BookModels", "BookRating"); } }
There we can see the Up
and Down
methods that will be invoked when needed. We can modify them to do what we want. For example, we will rate the books with a rating of five by default. We will modify the AddColumn
function with the following code:
AddColumn("dbo.BookModels", "BookRating",c =>c.Int(nullable:false, defaultValue:5));
The only thing left now is to update the database with the Update-Database
command. Go on and execute it.
To validate that everything has gone as expected, we should open the Server Explorer panel, navigate to our database, and finally, right-click on our table, selecting the Show Table Data option. We can appreciate that the BookRating column has been added and also that its value has been set to 5 as shown in the following screenshot:
Now we are going to go back to the previous version. To do this, we will use the Update-Database
command together with the –TargetMigration
flag. We will execute the following code on the Package Manager Console window:
Update-Database -TargetMigration:"InitialCreate"
This will not execute properly as it will produce a loss of data. We must add the –Force
flag to execute it without problems. Excecute the code previously mentioned, once again, but using the –Force
flag:
Update-Database -TargetMigration:"InitialCreate" -Force
We can again update the database to the latest state with the Update-Database -TargetMigration:"BookRating"
command. We don't have any reason to use the -Force
flag since there will be no data loss in this case.
We enabled Code First Migrations with the Enable-Migrations
command and manually changed the mode to Automatic Migrations, showing its behavior with a simple property change.
With Automatic Migrations, we can execute the Update-Database
command, and all is done for us, without any need to handle versions or anything else. We saw that this comes with the drawback of not being able to move to a previous version.
Continuing, we activated Code Migrations and generated a migration with the Add-Migration
command, which, as we saw, we can use to move up and down within the different versions, and finally, we customized a bit of migration to provide a default value.
Next, we saw how to move back and forward in time to a specific migration in a very easy way and also used the -Force
flag to enable proper downgrading in cases where the elimination of fields and/or tables is required. For this, the command is Update-Database –TargetMigration:"MigrationName"
, followed by the -Force
flag, if needed.
In addition, to what we have seen, we might want to perform some activity with the data to fit the new model, such as copying data from one table to another, prepopulating the new entity with a default value, and likewise. To do this, we can execute SQL at any point in our migration, as we can see in the following code:
Sql("UPDATE dbo.BookModels SET BookRating = 5");
Additionally we can automatically generate the update script with the –Script
flag indicating the source and the destination migrations:
Update-Database -Script -SourceMigration:$InitialDatabase - TargetMigration:"BookRating"
Executing this would generate the necessary SQL script that we can then use to execute the migration directly on our SQL Server.