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, 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 the changes in the database?
This article will provide the answers for 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.
Our Roadmap Towards Learning MVC with Entity Framework
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 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 choose that 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.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
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()
{
HasKey(t => t.Id);
Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(t => t.Name);
Property(t => t.Age);
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:
<connectionStrings>
<add name="DbConnectionString" connectionString="Data Source=sandeepss-PC;
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;
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 1.2: Database Created
Now we update the data model by addiing 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()
{
HasKey(t => t.Id);
Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(t => t.Name);
Property(t => t.Age);
Property(t => t.IsCurrent);
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 1.3: 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 a solution to resolve this error that don't reinitialize context so we define a static constructor in context class and set database initializer null
in it. Let's see the following updated code snippet of context class:
using System.Data.Entity;
using EF.Data.Mapping;
namespace EF.Data
{
public class EFDbContext : DbContext
{
static EFDbContext()
{
Database.SetInitializer<EFDbContext>(null);
}
public EFDbContext()
: base("name=DbConnectionString")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new StudentMap());
}
}
}
Now we run the application and get this error.
Figure 1.4 Database Update Exception
The preceding error occurs because our data model entity is updated in code while related database table is not updated. 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 1.5: Enable code first migration
This command adds a new folder, Migrations, in the project EF.Data
and this folder contains 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)
{
}
}
}
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 1.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 1.7.
SELECT [Id],[Name],[Age],[IsCurrent]FROM [EFCodeFirst].[dbo].[Students]
Figure 1.7: Retrieve data from 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 or directly connect by https://twitter.com/ss_shekhawat.