Share via


Code First Migrations in Entity Framework

Introduction

The previous article “Relationship in Entity Framework Using Code First Approach With Fluent API” introduced relationships of entities but when you read that article then some questions will occur to you such as:

  1. Should I create a database each time?
  2. How can I add / remove a field from an existing table?
  3. How can I avoid data loss when a field is added or removed from an existing table?
  4. Can I get a database script that has the changes in the database?

This article will provide the answers to all those questions. As you saw in the previous article, you add some entity classes to a project and it might be that you add or remove fields from entities. That's why the data model changes frequently. We have some configuration options in Entity Framework to create a database, such as create a database if it does not exist, automatically drop and re-create the database each time when you change an entity or data model.

Suppose you have first the configuration option that creates a database if it does not exist and now you add a new entity and run the application. Then you get an error such as Database "xx" cannot be created because it already exists but when you use the second configuration option and you add or remove a field from the entity or change entity classes or make a change in a DbContext class, the next time the application is run it automatically deletes the existing database, creates a new one that matches the model and seeds it with test data.

The Entity Framework migration feature enables us to change the entity or data model and deploy these changes to the database server by updating the database schema without having to drop and re-create the database.

Create Database from Data Model

To understand the migrations in the Entity Framework Code First approach, we create an entity and define their configuration using the Fluent API. We will create two class library projects, one library project (EF.Core) has entities and another project (EF.Data) has these entities configuration with DbContext.

Let's define a very simple data model. We are just defining it in the EF.Core project. Below the Employee class definition is the Employee.cs file under the EF.Core project.

using System;
 
namespace EF.Core
{
    public class  Employee
    {
        public Int64 EmployeeId { get; set; }
        public string  FirstName { get; set; }
        public string  LastName { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string  Address { get; set; }
        public DateTime CreatedDate { get; set; }
        public string  IPAddress { get; set; }        
    }
}

First of all we install the Entity Framework package using Nuget package manager to the EF.Data project so we can use it.

Figure 1: Install Entity Framework

We add a reference of the EF.Core project DLL to the EF.Data project so that we could use the data model to create the database table. Thereafter we define the configuration for the preceding data model that will be used when the database table is created. The configuration defines another class library project EF.Data. For the Employee data model we create the EmployeeMap configuration class definition in the EmployeeMap.cs file under the EF.Data project.

using EF.Core;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
 
namespace EF.Data
{
    public class  EmployeeMap : EntityTypeConfiguration<Employee>
    {
        public EmployeeMap()
        {
            HasKey(t => t.EmployeeId);
            Property(t => t.EmployeeId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            Property(t => t.FirstName).HasMaxLength(100);
            Property(t => t.LastName).HasMaxLength(100);
            Property(t => t.DateOfBirth).IsRequired();
            Property(t => t.Address).HasMaxLength(200);
            Property(t => t.CreatedDate).IsRequired();
            Property(t => t.IPAddress).IsRequired();           
            ToTable("Employees");
        }
    }
}

Now define the connection string in the App.config file under the EF.Data Project so that we can create a database with an appropriate name. The connection string is:

<connectionStrings>
    <add name="DbConnectionString" connectionString="Data Source=ADMIN\SQLEXPRESS;Initial Catalog=EFCodeFirst;User ID=sa; Password=" providerName="System.Data.SqlClient" />
  </connectionStrings>

Now we create a context class EFDbContext (in EFDbContext.cs) that inherits the DbContext class. In this class, we override the OnModelCreating() method. This method is called when the model for a context class (EFDbContext) has been initialized, but before the model has been locked down and used to initialize the context such that the model can be further configured before it is locked down. The following is the code snippet for the context class.

using System.Data.Entity;
 
namespace EF.Data
{
    public class  EFDbContext : DbContext
    {
        public EFDbContext() : base("name=DbConnectionString")
        {
 
        }
 
        protected override  void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new EmployeeMap());
        }
    }
}

As you know the EF Code First approach follows convention over configuration so in the constructor we just pass the connection string name the same as an App.Config file and it connects to that server. In the OnModelCreating() method, we add a configuration class object to DbModelBuilder.

We create a console application EF.Console to create a database and insert data into the database table. Implement the Main method in Program.cs as shown below. This code creates a new instance of our context and then uses it to insert new Employee.

using EF.Core;
using EF.Data;
using System;
using System.Data.Entity;
 
namespace EF.Console
{
    class Program
    {
        static void  Main(string[] args)
        {
            System.Console.Write("Enter employee first name : ");
            string firstName = System.Console.ReadLine();
            System.Console.Write("Enter employee last name : ");
            string lastName = System.Console.ReadLine();
            System.Console.Write("Enter employee Date of Birth : ");
            DateTime dateOfBirth = DateTime.UtcNow;
            DateTime.TryParse(System.Console.ReadLine(), out  dateOfBirth);
            System.Console.Write("Enter employee address : ");
            string address = System.Console.ReadLine();
            
            using (EFDbContext context = new EFDbContext())
            {
                Employee employee = new  Employee
                {
                    FirstName = firstName,
                    LastName = lastName,
                    DateOfBirth = dateOfBirth,
                    Address = address,
                    CreatedDate = DateTime.UtcNow,
                    IPAddress = "192.168.0.1"                   
                };
                context.Entry(employee).State = EntityState.Added;
                context.SaveChanges();
            }
            System.Console.ReadLine();
        }
    }
}

Now we run the preceding code and pass required values as per following figure.

Figure 2: Run the application

Now login to SQL Server and see that the database has been created and inserted a row into the Employee table.

Figure 3: Database created

Now we update the data model by adding a new field HourlyRate in it so the update of the Employee class will be such as:

using System;
 
namespace EF.Core
{
    public class  Employee
    {
        public Int64 EmployeeId { get; set; }
        public string  FirstName { get; set; }
        public string  LastName { get; set; }
        public DateTime DateOfBirth { get; set; }
        public string  Address { get; set; }
        public DateTime CreatedDate { get; set; }
        public string  IPAddress { get; set; }
        public Decimal HourlyRate { get; set; }
    }
}

As per the preceding additional field we also need to update its configuration class EmployeeMap such as:

using EF.Core;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
 
namespace EF.Data
{
    public class  EmployeeMap : EntityTypeConfiguration<Employee>
    {
        public EmployeeMap()
        {
            HasKey(t => t.EmployeeId);
            Property(t => t.EmployeeId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
            Property(t => t.FirstName).HasMaxLength(100);
            Property(t => t.LastName).HasMaxLength(100);
            Property(t => t.DateOfBirth).IsRequired();
            Property(t => t.Address).HasMaxLength(200);
            Property(t => t.CreatedDate).IsRequired();
            Property(t => t.IPAddress).IsRequired();
            Property(t => t.HourlyRate);
            ToTable("Employees");
        }
    }
}

Thereafter we update the Main method of Program.cs so that we can insert a value for the Employee’s hourly rate.

using EF.Core;
using EF.Data;
using System;
using System.Data.Entity;
 
namespace EF.Console
{   
    class Program
    {
        static void  Main(string[] args)
        {
            System.Console.Write("Enter employee first name : ");
            string firstName = System.Console.ReadLine();
            System.Console.Write("Enter employee last name : ");
            string lastName = System.Console.ReadLine();
            System.Console.Write("Enter employee Date of Birth : ");
            DateTime dateOfBirth = DateTime.UtcNow;
            DateTime.TryParse(System.Console.ReadLine(), out  dateOfBirth);
            System.Console.Write("Enter employee address : ");
            string address = System.Console.ReadLine();
            System.Console.Write("Enter employee hourly rate : ");
            decimal hourlyRate = 0.0m;
            Decimal.TryParse(System.Console.ReadLine(), out  hourlyRate);
 
            using (EFDbContext context = new EFDbContext())
            {
                Employee employee = new  Employee
                {
                    FirstName = firstName,
                    LastName = lastName,
                    DateOfBirth = dateOfBirth,
                    Address = address,
                    CreatedDate = DateTime.UtcNow,
                    IPAddress = "192.168.0.1",
                    HourlyRate = hourlyRate
                };
                context.Entry(employee).State = EntityState.Added;
                context.SaveChanges();
            }
            System.Console.ReadLine();
        }
    }
}

Now we run the application and get this error.


Figure 4: Error to add a record to database

The preceding error shows that the data model has been changed since the database was created and it's right. We have two solutions to resolve this error, either delete the database or use migrations. The first one is not useful since in that case, we lose all the data from the database so we will see the second solution in the next section.

Code First Migrations

The Code First Migration is used to update the database. Here we will look at how to use it in our application. Let's see it step-by-step.

From the Tools menu, click Library Package Manager and then Package Manager Console then choose the default project EF.Data in it. That means always choose the project with your context class for migrations.

At the PM> prompt enter the following command:

PM> Enable-Migrations

When running the preceding command you will get a console window such as:

Figure 5: Enable code first migration

This command adds a new folder, Migrations, in the project EF.Data and this folder contain a configuration file with default settings.

Now we add to the configuration settings in the Configuration class constructor, one to allow migration and another for no data loss when migrating. The excerpt of the Configuration class for these properties is:

AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = false;

We set the AutomaticMigrationEnabled property to true; that means we are using automatic code first migration and another property AutomaticMigrationDataLossAllowed is set to false. That means that during the migration no existing data is lost from that migration of the table of the database. The entire Configuration class is as follows.

namespace EF.Data.Migrations
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Migrations;
    using System.Linq;
 
    internal sealed  class Configuration : DbMigrationsConfiguration<EF.Data.EFDbContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = true;
            AutomaticMigrationDataLossAllowed = false;
            ContextKey = "EF.Data.EFDbContext";            
        }
 
        protected override  void Seed(EF.Data.EFDbContext context)
        {
            //  This method will be called after migrating to the latest version.
 
            //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
        }
    }
}

The seed method is used to insert default values into the database table.

Thereafter we will update the database using the Package Manager Console. To update the database at the PM> prompt enter the following command:

PM> Update-Database -Verbose

The "-Verbose" flag specifies to show the SQL Statements being applied to the target database in the console. You get results as in the following figure in the Package Manager Console.


Figure 6: Update existing database

Now we check the database and add a new record using our console application. We find that in the database there is no data loss and the application is running smoothly without throwing any exception. You can write the following query in the database and get the results as in Figure 7.

SELECT [EmployeeId],[FirstName],[LastName],[DateOfBirth],[Address],[CreatedDate],[IPAddress],[HourlyRate] FROM  [EFCodeFirst].[dbo].[Employees]

Figure 7: Retrieve data from Employees table

Download

There are source code available which belongs to the Entity Framework code first approach. These are as follows:

See Also

I would like to recommend two more articles which describe Entity Framework Code First approach basics. These are:

  1. Relationship in Entity Framework Using Code First Approach With Fluent API
  2. CRUD Operations in MVC using Generic Repository Pattern and Entity Framework

Conclusion

This article introduced Code First Migrations using Entity Framework. I hope that these two articles will provide a clearer understanding of how to use the Entity Framework in our application. If you have any concerns, post as a comment.