Introduction to EntityFramework 6 Part 2: migrations

Introduction

In the previous post we’ve created our first DbSet of Cars. We’ve also looked at the absolute basics of EntityFramework in an MVC5 project.

It’s now time to let EF create a database and a table for us where we can store the car objects. The current version of EF at the time of writing this post allows us to have multiple migration folders and DB contexts. Previous releases only allowed for one of each.

Demo

Open the Cars web project we started working on before. Open the Package manager console – View, Other windows, Package manager console – and issue the following command to enable migrations:

enable-migrations

If all went well then you should get an exception:

More than one context type was found in the assembly ‘Cars.Web’.
To enable migrations for ‘Cars.Web.Database.CarsDbContext’, use Enable-Migrations -ContextTypeName Cars.Web.Database.CarsDbContext.
To enable migrations for ‘Cars.Web.Database.UserManagementDbContext’, use Enable-Migrations -ContextTypeName Cars.Web.Database.UserManagementDbContext.

This is expected as we have 2 Db context types in the project as the message says: CarsDbContext and UserManagementDbContext. The compiler has found them both. The error message tells us quite clearly that we have to use the ContextTypeName flag to specify the correct context. Run the following command:

enable-migrations -ContextTypeName UserManagementDbContext -MigrationsDirectory Database\UserManagementMigrations

The simple class name was enough as there’s no other class with the same name, i.e. it’s unique. The MigrationsDirectory specifies where the migrations folder will be created for the user management context. Without this argument the migrations folder would be created at the root of the project and a directory can only have a single migrations folder. As we have 2 DB contexts, we want to have 2 migrations folders as well: one for users and another one for our domains.

If all went well then you should get the following message in the console:

Checking if the context targets an existing database…
Code First Migrations enabled for project Cars.Web.

You’ll see that the UserManagementMigrations folder was created within the Database folder with a file called Configuration.cs which specifies a migrations directory:

MigrationsDirectory = @"Database\UserManagementMigrations";

Let’s create the Cars migrations folder as well with the following command:

enable-migrations -ContextTypeName CarsDbContext -MigrationsDirectory Database\DomainMigrations

Check if the folder and the new Configuration.cs file have been created correctly.

Note that at this point there’s no database yet. The enable-migrations command checks if there’s an existing database under the connection string given in the constructor of the context object: DefaultConnection. As there’s none it just continues without creating a database.

Note that both Configuration files have a property called AutomaticMigrationsEnabled and it’s set to false. This means that if we change the structure of our context classes, such as adding a new property to Car, then we’ll need to specifically declare the change in the package manager console. This is a good idea in a production environment where you probably don’t want to change the database structure without advance planning.

As we have no database yet at all let’s create it using the add-migration command as the first step. It accepts a parameter called ConfigurationTypeName which specifies the Configuration class for the migration you want to run. This is the fully qualified name of the Configuration class. We’ll set up the user related tables first in code by executing the following command in the package manager console:

add-migration -ConfigurationTypeName Cars.Web.Database.UserManagementMigrations.Configuration “FirstInitialisation”

That’s the fully qualified name of Configuration in the Database\UserManagementMigrations folder. FirstInitialisation is the name of the migration. If all went well then you’ll be presented a class called FirstInitialisation in a file called [some date value]_FirstInitialisation.cs. It derives from DbMigration which is a base class for code-based migrations. It’s normal C# code and quite straightforward to follow. You’ll see field declarations with their types, lengths, primary keys, foreign keys, indexes etc., e.g.:

CreateTable(
                "dbo.AspNetUserRoles",
                c => new
                    {
                        UserId = c.String(nullable: false, maxLength: 128),
                        RoleId = c.String(nullable: false, maxLength: 128),
                    })
                .PrimaryKey(t => new { t.UserId, t.RoleId })
                .ForeignKey("dbo.AspNetRoles", t => t.RoleId, cascadeDelete: true)
                .ForeignKey("dbo.AspNetUsers", t => t.UserId, cascadeDelete: true)
                .Index(t => t.RoleId)
                .Index(t => t.UserId);

Note that there’s a series on Identity on this blog starting here which goes through the user management tables and how to use them.

These CreateTable and other API calls, like CreateIndex and CreateStoredProcedure are wrappers around SQL statements.

Let’s also initialise the Cars DB:

add-migration -ConfigurationTypeName Cars.Web.Database.DomainMigrations.Configuration “FirstInitialisation”

This will create a FirstInitialisation class for our domains. There’s only the Car domain at preset so the single CreateTable statement is quite simple. The Id will be set as the primary key. Note that maxlength of Make is set to 50 which was extracted from the StringLength attribute we set on the Make property. The category enumeration is stored as a non-nullable integer. So there’s no separate mapping table for the enumeration values. The Cars table will simply include a column called Category with numeric codes for the enumerations. This can be both good and bad: you don’t need to get the string information from another table, but you cannot readily update the descriptions in the database either.

Notice that there’s still no database that reflects our user and car objects. The update-database package console command can do that. It will first check if there’s any existing database and apply all migration scripts that had not been processed up to that point. Otherwise it will create a new database and run all available migration scripts. The ConfigurationTypeName parameter can again be set to show which migration to run. Run the following command to create the user tables:

update-database -ConfigurationTypeName Cars.Web.Database.UserManagementMigrations.Configuration

Refresh the solution and click the Show All Files option in the solution explorer. You should see that there’s a new MDF file within the App_Data folder called aspnet-Cars.Web-somedatestamp.mdf.

Let’s also create the Cars table:

update-database -ConfigurationTypeName Cars.Web.Database.DomainMigrations.Configuration

In case you want to see the actual SQL commands sent to the database you’d need to add the -verbose flag to the update-database command:

update-database -ConfigurationTypeName Cars.Web.Database.DomainMigrations.Configuration -verbose

Double-click the mdf file in Solution Explorer to view the contents. This will open the Server Explorer window where you can see the tables created:

Update-database command executed

The user manager tables will look familiar from the series on Identity referred to above. Also, we have the Cars table with the Id, Make and Category fields.

You are of course not limited to creating the database in the built-in local SQL server. Update the connection string in web.config as you wish and the changes will be reflected in the next update-database command.

In the next post we’ll start adding Car objects to the Cars table.

View the posts related to data storage here.

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

Leave a comment

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.