Code First Migrations With The Entity Framework in An ASP
Code First Migrations With The Entity Framework in An ASP
The Contoso University sample web application demonstrates how to create ASP.NET MVC 5
applications using the Entity Framework 6 Code First and Visual Studio 2013
Enable Code First Migrations. The Migrations feature enables you to change the data
model and deploy your changes to production by updating the database schema
without having to drop and re-create the database.
This method of keeping the database in sync with the data model works well until you deploy
the application to production. When the application is running in production it is usually storing
data that you want to keep, and you don't want to lose everything each time you make a
change such as adding a new column. The Code First Migrations feature solves this problem by
enabling Code First to update the database schema instead of dropping and re-creating the
database. In this tutorial, you'll deploy the application, and to prepare for that you'll enable
Migrations.
1. Disable the initializer that you set up earlier by commenting out or deleting the contexts
element that you added to the application Web.config file.
<entityFramework>
<!--<contexts>
<context type="ContosoUniversity.DAL.SchoolContext, ContosoUniversity">
<databaseInitializer type="ContosoUniversity.DAL.SchoolInitializer,
ContosoUniversity" />
</context>
</contexts>-->
<defaultConnectionFactory
type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
<parameters>
<parameter value="v11.0" />
</parameters>
</defaultConnectionFactory>
<providers>
<provider invariantName="System.Data.SqlClient"
type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
</providers>
</entityFramework>
2. Also in the application Web.config file, change the name of the database in the
connection string to ContosoUniversity2.
<connectionStrings>
<add name="SchoolContext" connectionString="Data Source=(LocalDb)\v11.0;Initial
Catalog=ContosoUniversity2;Integrated Security=SSPI;"
providerName="System.Data.SqlClient" />
</connectionStrings>
This change sets up the project so that the first migration will create a new database.
This isn't required but you'll see later why it's a good idea.
3. From the Tools menu, click Library Package Manager and then Package Manager
Console.
4. At the PM> prompt enter the following commands:
enable-migrations
add-migration InitialCreate
(If you missed the step above that directs you to change the database name, Migrations
will find the existing database and automatically do the add-migration command. That's
OK, it just means you won't run a test of the migrations code before you deploy the
database. Later when you run the update-database command nothing will happen because
the database will already exist.)
Like the initializer class that you saw earlier, the Configuration class includes a Seed
method.
The purpose of the Seed method is to enable you to insert or update test data after
Code First creates or updates the database. The method is called when the database is
created and every time the database schema is updated after a data model change.
When you are dropping and re-creating the database for every data model change, you use the
initializer class's Seed method to insert test data, because after every model change the
database is dropped and all the test data is lost. With Code First Migrations, test data is
retained after database changes, so including test data in the Seed method is typically not
necessary. In fact, you don't want the Seed method to insert test data if you'll be using
Migrations to deploy the database to production, because the Seed method will run in
production. In that case you want the Seed method to insert into the database only the data
that you need in production. For example, you might want the database to include actual
department names in the Department table when the application becomes available in
production.
For this tutorial, you'll be using Migrations for deployment, but your Seed method will insert test
data anyway in order to make it easier to see how application functionality works without
having to manually insert a lot of data.
1. Replace the contents of the Configuration.cs file with the following code, which will load
test data into the new database.
namespace ContosoUniversity.Migrations
{
using ContosoUniversity.Models;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
The Seed method takes the database context object as an input parameter, and the
code in the method uses that object to add new entities to the database. For each entity
type, the code creates a collection of new entities, adds them to the appropriate DbSet
property, and then saves the changes to the database. It isn't necessary to call the
SaveChanges method after each group of entities, as is done here, but doing that helps
you locate the source of a problem if an exception occurs while the code is writing to
the database.
Some of the statements that insert data use the AddOrUpdate method to perform an
"upsert" operation. Because the Seed method runs every time you execute the update-
database command, typically after each migration, you can't just insert data, because the
rows you are trying to add will already be there after the first migration that creates the
database. The "upsert" operation prevents errors that would happen if you try to insert
a row that already exists, but it overrides any changes to data that you may have made
while testing the application. With test data in some tables you might not want that to
happen: in some cases when you change data while testing you want your changes to
remain after database updates. In that case you want to do a conditional insert
operation: insert a row only if it doesn't already exist. The Seed method uses both
approaches.
The first parameter passed to the AddOrUpdate method specifies the property to use to
check if a row already exists. For the test student data that you are providing, the
LastName property can be used for this purpose since each last name in the list is unique:
This code assumes that last names are unique. If you manually add a student with a
duplicate last name, you'll get the following exception the next time you perform a
migration.
For information about how to handle redundant data such as two students named
"Alexander Carson", see Seeding and Debugging Entity Framework (EF) DBs on Rick
Anderson's blog. For more information about the AddOrUpdate method, see Take care
with EF 4.3 AddOrUpdate Method on Julie Lerman's blog.
The code that creates Enrollment entities assumes you have the ID value in the entities in
the students collection, although you didn't set that property in the code that creates the
collection.
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").ID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
Grade = Grade.A
},
You can use the ID property here because the ID value is set when you call SaveChanges for
the students collection. EF automatically gets the primary key value when it inserts an
entity into the database, and it updates the ID property of the entity in memory.
The code that adds each Enrollment entity to the Enrollments entity set doesn't use the
AddOrUpdate method. It checks if an entity already exists and inserts the entity if it
doesn't exist. This approach will preserve changes you make to an enrollment grade by
using the application UI. The code loops through each member of the Enrollment List and
if the enrollment is not found in the database, it adds the enrollment to the database.
The first time you update the database, the database will be empty, so it will add each
enrollment.
When you executed the add-migration command, Migrations generated the code that would
create the database from scratch. This code is also in the Migrations folder, in the file named
<timestamp>_InitialCreate.cs. The Up method of the InitialCreate class creates the database
tables that correspond to the data model entity sets, and the Down method deletes them.
namespace ContosoUniversity.Migrations
{
using System;
using System.Data.Entity.Migrations;
CreateTable(
"dbo.Enrollment",
c => new
{
EnrollmentID = c.Int(nullable: false, identity: true),
CourseID = c.Int(nullable: false),
StudentID = c.Int(nullable: false),
Grade = c.Int(),
})
.PrimaryKey(t => t.EnrollmentID)
.ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
.ForeignKey("dbo.Student", t => t.StudentID, cascadeDelete: true)
.Index(t => t.CourseID)
.Index(t => t.StudentID);
CreateTable(
"dbo.Student",
c => new
{
ID = c.Int(nullable: false, identity: true),
LastName = c.String(),
FirstMidName = c.String(),
EnrollmentDate = c.DateTime(nullable: false),
Secret = c.String(),
})
.PrimaryKey(t => t.ID);
Migrations calls the Up method to implement the data model changes for a migration. When
you enter a command to roll back the update, Migrations calls the Down method.
This is the initial migration that was created when you entered the add-migration InitialCreate
command. The parameter (InitialCreate in the example) is used for the file name and can be
whatever you want; you typically choose a word or phrase that summarizes what is being done
in the migration. For example, you might name a later migration "AddDepartmentTable".
If you created the initial migration when the database already exists, the database creation
code is generated but it doesn't have to run because the database already matches the data
model. When you deploy the app to another environment where the database doesn't exist
yet, this code will run to create your database, so it's a good idea to test it first. That's why you
changed the name of the database in the connection string earlier -- so that migrations can
create a new one from scratch.
update-database
The update-database command runs the Up method to create the database and then it
runs the Seed method to populate the database. The same process will run automatically
in production after you deploy the application, as you'll see in the following section.
2. Use Server Explorer to inspect the database as you did in the first tutorial, and run the
application to verify that everything still works the same as before.