Introduction
My 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.
- Should I create a database each time?
- How can I add/remove a field from an existing table?
- How can I avoid data loss when a field is added or removed from an existing table?
- Can I get a database script that has changes in the database?
This article will provide 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 creating a database if it does not exist, and automatically dropping and re-creating 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 dropping and re-creating the database.
Create a database from the Data Model
To understand the migrations in the Entity Framework Code First approach, we create an entity and define its configuration using the Fluent API. We will create two class library projects, one library project (EF.Core) has entities, and another (EF.Data) has these entities configured with DbContext. Let's define a very simple data model. We are just defining it in the EF.Core project. Below the Student class definition is the Student.cs file under the EF—core project.
using System;
namespace EF.Core
{
public class Student
{
public Int64 Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
}
First of all, we install the Entity Framework package to the EF.Data project so we can use it.
From the Tools menu, click Library Package Manager and then Package Manager Console then choose default project EF.Data in it, which means always choosing the project where you want to install a package.
At the PM> prompt enter the following command
PM> Install-Package EntityFramework -Version 5.0.0
Figure 1. Install Entity Framework
We add a reference to 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 under the Mapping folder. For the Student data model, we create the StudentMap configuration class definition in the StudentMap.cs file under the EF.Data project.
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
using EF.Core;
namespace EF.Data.Mapping
{
public class StudentMap : EntityTypeConfiguration<Student>
{
public StudentMap()
{
//key
HasKey(t => t.Id);
//property
Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(t => t.Name);
Property(t => t.Age);
//table
ToTable("Students");
}
}
}
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 connectionstring is.
<configuration>
<connectionStrings>
<add name="DbConnectionString" connectionString="Data Source=sandeepss-PC;Initial Catalog=EFCodeFirst;User ID=sa; Password=****" providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
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;
using EF.Data.Mapping;
namespace EF.Data
{
public class EFDbContext : DbContext
{
public EFDbContext()
: base("name=DbConnectionString")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new StudentMap());
}
}
}
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 a new Student.
using System;
using EF.Core;
using EF.Data;
namespace EF.Console
{
class Program
{
static void Main(string[] args)
{
System.Console.Write("Enter your name: ");
string name = System.Console.ReadLine();
System.Console.Write("Enter your age: ");
int age = 0;
Int32.TryParse(System.Console.ReadLine(), out age);
using (EFDbContext context = new EFDbContext())
{
Student student = new Student { Name = name, Age = age };
context.Entry(student).State = System.Data.EntityState.Added;
context.SaveChanges();
}
System.Console.ReadLine();
}
}
}
Now we run the preceding code and get the result that the database was created in SQL Server and inserted a row into the Student table.
Figure 2. Database Created
Now we update the data model by adding a new field IsCurrent in it so the update of the Student class will be such as.
using System;
namespace EF.Core
{
public class Student
{
public Int64 Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool IsCurrent { get; set; }
}
}
As per the preceding explanation we also need to update its configuration class StudentMap such as.
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
using EF.Core;
namespace EF.Data.Mapping
{
public class StudentMap : EntityTypeConfiguration<Student>
{
public StudentMap()
{
//key
HasKey(t => t.Id);
//property
Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(t => t.Name);
Property(t => t.Age);
Property(t => t.IsCurrent);
//table
ToTable("Students");
}
}
}
Thereafter we update the Main method of Program.cs so that we can insert a value for the student as the current student or not.
using System;
using EF.Core;
using EF.Data;
namespace EF.Console
{
class Program
{
static void Main(string[] args)
{
System.Console.Write("Enter your name : ");
string name = System.Console.ReadLine();
System.Console.Write("Enter your age : ");
int age = 0;
Int32.TryParse(System.Console.ReadLine(), out age);
System.Console.Write("You are current student");
bool isCurrent = System.Console.ReadLine() == "Yes" ? true : false;
using (EFDbContext context = new EFDbContext())
{
Student student = new Student { Name = name, Age = age, IsCurrent = isCurrent };
context.Entry(student).State = System.Data.EntityState.Added;
context.SaveChanges();
}
System.Console.ReadLine();
}
}
}
Now we run the application and get this error.
Figure 3. Error in adding a record to the 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 choosing 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 4. 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;
}
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 enters 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 5. 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 5.
SELECT [Id], [Name], [Age], [IsCurrent]
FROM [EFCodeFirst].[dbo].[Students]
Figure 6. Retrieve data from the student table.
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.