1 5_Code First Migrations and Deployment with the Entity Framework in an ASP.NET MVC Application Mon Jul 06, 2015 8:08 am
Admin
Admin
So far the application has been running locally in IIS Express on your development computer. To make a real application available for other people to use over the Internet, you have to deploy it to a web hosting provider. In this tutorial you'll deploy the Contoso University application to the cloud in Azure.
The tutorial contains the following sections:
- 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.
- Deploy to Azure. This step is optional; you can continue with the remaining tutorials without having deployed the project.
We recommend that you use a continuous integration process with source control for deployment, but this tutorial does not cover those topics. For more information, see the source control and continuous integration chapters of the Building Real-World Cloud Apps with Azure e-book.
Enable Code First Migrations
When you develop a new application, your data model changes frequently, and each time the model changes, it gets out of sync with the database. You have configured the Entity Framework to automatically drop and re-create the database each time you change the data model. When you add, remove, or change entity classes or change your
- Code:
DbContext
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.
[list defaultattr=]
[*]Disable the initializer that you set up earlier by commenting out or deleting the
- Code:
contexts
[*]Also in the application Web.config file, change the name of the database in the connection string to ContosoUniversity2.
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.
[*]From the Tools menu, click Library Package Manager and then Package Manager Console.
[*]At the
- Code:
PM>
- Code:
enable-migrations
- Code:
add-migration InitialCreate
The
- Code:
enable-migrations
(If you missed the step above that directs you to change the database name, Migrations will find the existing database and automatically do the
- Code:
add-migration
- Code:
update-database
Like the initializer class that you saw earlier, the
- Code:
Configuration
- Code:
Seed
internal sealed class Configuration : DbMigrationsConfiguration
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet
// 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 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.
[/list]
Set up the Seed Method
When you are dropping and re-creating the database for every data model change, you use the initializer class's
- Code:
Seed
- Code:
Seed
- Code:
Seed
- Code:
Seed
- Code:
Department
For this tutorial, you'll be using Migrations for deployment, but your
- Code:
Seed
[list defaultattr=]
[*]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;
internal sealed class Configuration : DbMigrationsConfiguration
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
{
var students = new List
{
new Student { FirstMidName = "Carson", LastName = "Alexander",
EnrollmentDate = DateTime.Parse("2010-09-01") },
new Student { FirstMidName = "Meredith", LastName = "Alonso",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Arturo", LastName = "Anand",
EnrollmentDate = DateTime.Parse("2013-09-01") },
new Student { FirstMidName = "Gytis", LastName = "Barzdukas",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Yan", LastName = "Li",
EnrollmentDate = DateTime.Parse("2012-09-01") },
new Student { FirstMidName = "Peggy", LastName = "Justice",
EnrollmentDate = DateTime.Parse("2011-09-01") },
new Student { FirstMidName = "Laura", LastName = "Norman",
EnrollmentDate = DateTime.Parse("2013-09-01") },
new Student { FirstMidName = "Nino", LastName = "Olivetto",
EnrollmentDate = DateTime.Parse("2005-08-11") }
};
students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, s));
context.SaveChanges();
var courses = new List
{
new Course {CourseID = 1050, Title = "Chemistry", Credits = 3, },
new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3, },
new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3, },
new Course {CourseID = 1045, Title = "Calculus", Credits = 4, },
new Course {CourseID = 3141, Title = "Trigonometry", Credits = 4, },
new Course {CourseID = 2021, Title = "Composition", Credits = 3, },
new Course {CourseID = 2042, Title = "Literature", Credits = 4, }
};
courses.ForEach(s => context.Courses.AddOrUpdate(p => p.Title, s));
context.SaveChanges();
var enrollments = new List
{
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").ID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
Grade = Grade.A
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").ID,
CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
Grade = Grade.C
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alexander").ID,
CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").ID,
CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").ID,
CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Alonso").ID,
CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Anand").ID,
CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Anand").ID,
CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Barzdukas").ID,
CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Li").ID,
CourseID = courses.Single(c => c.Title == "Composition").CourseID,
Grade = Grade.B
},
new Enrollment {
StudentID = students.Single(s => s.LastName == "Justice").ID,
CourseID = courses.Single(c => c.Title == "Literature").CourseID,
Grade = Grade.B
}
};
foreach (Enrollment e in enrollments)
{
var enrollmentInDataBase = context.Enrollments.Where(
s =>
s.Student.ID == e.StudentID &&
s.Course.CourseID == e.CourseID).SingleOrDefault();
if (enrollmentInDataBase == null)
{
context.Enrollments.Add(e);
}
}
context.SaveChanges();
}
}
}
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
- Code:
Seed
- Code:
update-database
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
- Code:
LastName
context.Students.AddOrUpdate(p => p.LastName, s)
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.
Sequence contains more than one element
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
- Code:
AddOrUpdate
The code that creates
- Code:
Enrollment
- Code:
ID
- Code:
students
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
- Code:
ID
- Code:
ID
- Code:
SaveChanges
- Code:
students
- Code:
ID
The code that adds each
- Code:
Enrollment
- Code:
Enrollments
- Code:
AddOrUpdate
- Code:
Enrollment
foreach (Enrollment e in enrollments)
{
var enrollmentInDataBase = context.Enrollments.Where(
s => s.Student.ID == e.Student.ID &&
s.Course.CourseID == e.Course.CourseID).SingleOrDefault();
if (enrollmentInDataBase == null)
{
context.Enrollments.Add(e);
}
}
[*]Build the project.
[/list]
Execute the First Migration
When you executed the
- Code:
add-migration
- Code:
Up
- Code:
InitialCreate
- Code:
Down
public partial class InitialCreate : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Course",
c => new
{
CourseID = c.Int(nullable: false),
Title = c.String(),
Credits = c.Int(nullable: false),
})
.PrimaryKey(t => t.CourseID);
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),
})
.PrimaryKey(t => t.ID);
}
public override void Down()
{
DropForeignKey("dbo.Enrollment", "StudentID", "dbo.Student");
DropForeignKey("dbo.Enrollment", "CourseID", "dbo.Course");
DropIndex("dbo.Enrollment", new[] { "StudentID" });
DropIndex("dbo.Enrollment", new[] { "CourseID" });
DropTable("dbo.Student");
DropTable("dbo.Enrollment");
DropTable("dbo.Course");
}
}
Migrations calls the
- Code:
Up
- Code:
Down
This is the initial migration that was created when you entered the
- Code:
add-migration InitialCreate
- Code:
InitialCreate
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.
[list defaultattr=]
[*]In the Package Manager Console window, enter the following command:
- Code:
update-database
The
- Code:
update-database
- Code:
Up
- Code:
Seed
[*]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.
[/list]
Deploy to Azure
So far the application has been running locally in IIS Express on your development computer. To make it available for other people to use over the Internet, you have to deploy it to a web hosting provider. In this section of the tutorial you'll deploy it to Azure. This section is optional; you can skip this and continue with the following tutorial, or you can adapt the instructions in this section for a different hosting provider of your choice.
Using Code First Migrations to Deploy the Database
To deploy the database you'll use Code First Migrations. When you create the publish profile that you use to configure settings for deploying from Visual Studio, you'll select a check box labeled Execute Code First Migrations (runs on application start). This setting causes the deployment process to automatically configure the application Web.config file on the destination server so that Code First uses the
- Code:
MigrateDatabaseToLatestVersion
Visual Studio doesn't do anything with the database during the deployment process while it is copying your project to the destination server. When you run the deployed application and it accesses the database for the first time after deployment, Code First checks if the database matches the data model. If there's a mismatch, Code First automatically creates the database (if it doesn't exist yet) or updates the database schema to the latest version (if a database exists but doesn't match the model). If the application implements a Migrations
- Code:
Seed
Your Migrations
- Code:
Seed
- Code:
Seed
- Code:
Seed
- Code:
Seed
Get an Azure account
You'll need an Azure account. If you don't already have one, but you do have an MSDN subscription, you can activate your MSDN subscription benefits. Otherwise, you can create a free trial account in just a couple of minutes. For details, see Azure Free Trial.
Create a web site and a SQL database in Azure
Your web app in Azure will run in a shared hosting environment, which means it runs on virtual machines (VMs) that are shared with other Azure clients. A shared hosting environment is a low-cost way to get started in the cloud. Later, if your web traffic increases, the application can scale to meet the need by running on dedicated VMs.
You'll deploy the database to Azure SQL Database. SQL Database is a cloud-based relational database service that is built on SQL Server technologies. Tools and applications that work with SQL Server also work with SQL Database.
[list defaultattr=]
[*]In the Azure Management Portal, click Web Sites in the left tab, and then click New.
[*]Click CUSTOM CREATE.
The New Web Site - Custom Create wizard opens.
[*]In the New Web Site step of the wizard, enter a string in the URL box to use as the unique URL for your application. The complete URL will consist of what you enter here plus the suffix that you see next to the text box. The illustration shows "ConU", but that URL is probably taken so you will have to choose a different one.
[*]In the Region drop-down list, choose a region close to you. This setting specifies which data center your web site will run in.
[*]In the Database drop-down list, choose Create a free 20 MB SQL database.
[*]In the DB CONNECTION STRING NAME, enter SchoolContext.
[*]Click the arrow that points to the right at the bottom of the box. The wizard advances to the Database Settings step.
[*]In the Name box, enter ContosoUniversityDB.
[*]In the Server box, select New SQL Database server. Alternatively, if you previously created a server, you can select that server from the drop-down list.
[*]Enter an administrator LOGIN NAME and PASSWORD. If you selected New SQL Database server you aren't entering an existing name and password here, you're entering a new name and password that you're defining now to use later when you access the database. If you selected a server that you created previously, you’ll enter credentials for that server. For this tutorial, you won't select the Advanced check box. The Advanced options enable you to set the database collation.
[*]Choose the same Region that you chose for the web site.
[*]Click the check mark at the bottom right of the box to indicate that you're finished.
The Management Portal returns to the Web Sites page, and the Status column shows that the site is being created. After a while (typically less than a minute), the Status column shows that the site was successfully created. In the navigation bar at the left, the number of sites you have in your account appears next to the Web Sites icon, and the number of databases appears next to the SQL Databases icon.
[/list]
Deploy the application to Azure
[list defaultattr=]
[*]In Visual Studio, right-click the project in Solution Explorer and select Publish from the context menu.
[*]In the Profile tab of the Publish Web wizard, click Import.
[*]If you have not previously added your Azure subscription in Visual Studio, perform the following steps. These steps enable Visual Studio to connect to your Azure subscription so that the drop-down list under Import from Azure will include your web site.
As an alternative, you can sign in directly to your Azure account without downloading a subscription file. To use this method, click Sign In instead of Manage subscriptions in the next step. This alternative is simpler, but as this tutorial is being written in November, 2013, only the subscription file download enables Server Explorer to connect to Azure SQL Database.
a. In the Import Publish Profile dialog box, click Manage subscriptions.
b. In the Manage Azure Subscriptions dialog box, click the Certificates tab, and then click Import.
c. In the Import Azure Subscriptions dialog box, click Download subscription file .
d. In your browser window, save the .publishsettings file.
Security Note: The publishsettings file contains your credentials (unencoded) that are used to administer your Azure subscriptions and services. The security best practice for this file is to store it temporarily outside your source directories (for example in the Downloads folder), and then delete it once the import has completed. A malicious user who gains access to the
- Code:
.publishsettings
e. In the Import Azure Subscriptions dialog box, click Browse and navigate to the .publishsettings file.
e. Click Import.
[*]Close the Manage Azure Subscriptions box.
[*]In the Import Publish Profile dialog box, select Import from an Azure, select your web site from the drop-down list, and then click OK.
[*]In the Connection tab, click Validate Connection to make sure that the settings are correct.
[*]When the connection has been validated, a green check mark is shown next to the Validate Connection button. Click Next.
[*]Open the Remote connection string drop-down list under SchoolContext and select the connection string for the database you created.
[*]Select Execute Code First Migrations (runs on application start).
This setting causes the deployment process to automatically configure the application Web.config file on the destination server so that Code First uses the MigrateDatabaseToLatestVersion initializer class.
[*]Click Next.
[*]In the Preview tab, click Start Preview.
The tab displays a list of the files that will be copied to the server. Displaying the preview isn't required to publish the application but is a useful function to be aware of. In this case, you don't need to do anything with the list of files that is displayed. The next time you deploy this application, only the files that have changed will be in this list.
[*]Click Publish.
Visual Studio begins the process of copying the files to the Azure server.
[*]The Output window shows what deployment actions were taken and reports successful completion of the deployment.
[*]Upon successful deployment, the default browser automatically opens to the URL of the deployed web site.
The application you created is now running in the cloud. Click the Students tab.
[/list]
At this point your SchoolContext database has been created in the Azure SQL Database because you selected Execute Code First Migrations (runs on app start). The Web.config file in the deployed web site has been changed so that the MigrateDatabaseToLatestVersion initializer runs the first time your code reads or writes data in the database (which happened when you selected the Students tab):
The deployment process also created a new connection string (SchoolContext_DatabasePublish) for Code First Migrations to use for updating the database schema and seeding the database.
You can find the deployed version of the Web.config file on your own computer in ContosoUniversity\obj\Release\Package\PackageTmp\Web.config. You can access the deployed Web.config file itself by using FTP. For instructions, see ASP.NET Web Deployment using Visual Studio: Deploying a Code Update. Follow the instructions that start with "To use an FTP tool, you need three things: the FTP URL, the user name, and the password."
Note: The web app doesn't implement security, so anyone who finds the URL can change the data. For instructions on how to secure the web site, see Deploy a Secure ASP.NET MVC app with Membership, OAuth, and SQL Database to Azure. You can prevent other people from using the site by using the Azure Management Portal or Server Explorer in Visual Studio to stop the site.
Advanced Migrations Scenarios
If you deploy a database by running migrations automatically as shown in this tutorial, and you are deploying to a web site that runs on multiple servers, you could get mutiple servers trying to run migrations at the same time. Migrations are atomic, so if two servers try to run the same migration, one will succeed and the other will fail (assuming the operations can't be done twice). In that scenario if you want to avoid those issues, you can call migrations manually and set up your own code so that it only happens once. For more information, see Running and Scripting Migrations from Code on Rowan Miller's blog and Migrate.exe (for executing migrations from the command line) on MSDN.
For information about other migrations scenarios, see Migrations Screencast Series.
Code First Initializers
In the deployment section you saw the MigrateDatabaseToLatestVersion initializer being used. Code First also provides other initializers, including CreateDatabaseIfNotExists (the default), DropCreateDatabaseIfModelChanges (which you used earlier) and DropCreateDatabaseAlways. The
- Code:
DropCreateAlways
For more information about initializers, see Understanding Database Initializers in Entity Framework Code First and chapter 6 of the book Programming Entity Framework: Code First by Julie Lerman and Rowan Miller.
Summary
In this tutorial you've seen how to enable migrations and deploy the application. In the next tutorial you'll begin looking at more advanced topics by expanding the data model.
Please leave feedback on how you liked this tutorial and what we could improve. You can also request new topics at Show Me How With Code.
Links to other Entity Framework resources can be found in ASP.NET Data Access - Recommended Resources.
This article was originally created on March 7, 2014