教程:在 ASP.NET MVC 应用中使用 EF 迁移并部署到 Azure

到目前为止,Contoso University 示例 Web 应用程序已在开发计算机上IIS Express本地运行。 若要使真实应用程序可供其他人通过 Internet 使用,必须将其部署到 Web 托管提供商。 在本教程中,将启用 Code First 迁移并将应用程序部署到 Azure 中的云:

  • 启用Code First 迁移。 借助迁移功能,可以更改数据模型,并通过更新数据库架构将更改部署到生产环境,而无需删除并重新创建数据库。
  • 部署到 Azure。 此步骤是可选的;无需部署项目即可继续学习剩余教程。

建议将持续集成过程与源代码管理配合使用进行部署,但本教程未涵盖这些主题。 有关详细信息,请参阅使用 Azure 生成Real-World云应用的源代码管理和持续集成章节。

在本教程中,你将了解:

  • 启用 Code First 迁移
  • 在 Azure 中部署应用 (可选)

先决条件

启用 Code First 迁移

开发新应用程序时,数据模型会频繁更改。每当模型更改时,模型都无法与数据库保持同步。 已将 Entity Framework 配置为在每次更改数据模型时自动删除并重新创建数据库。 添加、删除或更改实体类或更改 DbContext 类时,下次运行应用程序时,它会自动删除现有数据库,创建一个与模型匹配的新数据库,并使用测试数据为其设定种子。

这种使数据库与数据模型保持同步的方法适用于多种情况,但将应用程序部署到生产环境的情况除外。 当应用程序在生产环境中运行时,它通常会存储要保留的数据,并且不希望每次进行更改(例如添加新列)时丢失所有内容。 Code First 迁移功能通过启用 Code First 来更新数据库架构而不是删除并重新创建数据库来解决此问题。 在本教程中,你将部署应用程序,并为此做好准备,你将启用迁移。

  1. 通过注释掉或删除 contexts 添加到应用程序Web.config文件的元素来禁用之前设置的初始值设定项。

    <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. 此外,在应用程序 Web.config 文件中,将连接字符串中的数据库名称更改为 ContosoUniversity2。

    <connectionStrings>
      <add name="SchoolContext" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;Initial Catalog=ContosoUniversity2;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
    </connectionStrings>
    

    此更改设置项目,以便第一次迁移创建新数据库。 这不是必需的,但稍后你将看到为什么这是一个好主意。

  3. 在“工具”菜单中,选择“NuGet 包管理器”>“包管理器控制台”。

  4. 在提示符处 PM> 输入以下命令:

    enable-migrations
    add-migration InitialCreate
    

    命令 enable-migrations 在 ContosoUniversity 项目中创建 一个 Migrations 文件夹,并在该文件夹中放入一个 Configuration.cs 文件,你可以编辑该文件来配置迁移。

    (如果错过了上述指示更改数据库名称的步骤,迁移将找到现有数据库并自动执行 add-migration 命令。没关系,这只是意味着在部署数据库之前不会运行迁移代码测试。稍后运行 update-database 命令时,不会发生任何操作,因为数据库已存在。)

    打开 ContosoUniversity\Migrations\Configuration.cs 文件。 与前面看到的初始值设定项类一样, Configuration 类包含 方法 Seed

    internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.DAL.SchoolContext>
    {
        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<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" }
            //    );
            //
        }
    }
    

    Seed 方法的目的是在 Code First 创建或更新数据库后插入或更新测试数据。 创建数据库时,每次在数据模型更改后更新数据库架构时,都会调用 方法。

设置 Seed 方法

删除并重新创建每个数据模型更改的数据库时,将使用初始值设定项类的 Seed 方法来插入测试数据,因为每次模型更改后,数据库都会被删除,并且所有测试数据都将丢失。 使用Code First 迁移,测试数据在数据库更改后保留,因此通常不需要在 Seed 方法中包含测试数据。 事实上,如果要使用迁移将数据库部署到生产环境,则不希望 Seed 方法插入测试数据,因为 Seed 该方法将在生产环境中运行。 在这种情况下,你希望 Seed 方法仅将生产中所需的数据插入数据库中。 例如,当应用程序在生产环境中可用时,你可能希望数据库在 Department 表中包括实际的部门名称。

在本教程中,你将使用迁移进行部署,但你的 Seed 方法无论如何都会插入测试数据,以便更轻松地查看应用程序功能的工作原理,而无需手动插入大量数据。

  1. Configuration.cs 文件的内容替换为以下代码,以将测试数据加载到新数据库中。

    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<ContosoUniversity.DAL.SchoolContext>
        {
            public Configuration()
            {
                AutomaticMigrationsEnabled = false;
            }
    
            protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
            {
                var students = new List<Student>
                {
                    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<Course>
                {
                    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<Enrollment>
                {
                    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();
            }
        }
    }
    

    Seed 方法将数据库上下文对象作为输入参数,方法中的代码使用该对象向数据库添加新实体。 对于每种实体类型,代码会创建一个新实体的集合,将它们添加到相应的 DbSet 属性,然后将更改保存到数据库。 不必像此处那样在每组实体之后调用 SaveChanges 方法,但如果代码写入数据库时发生异常,则这样做有助于找到问题的根源。

    插入数据的一些语句使用 AddOrUpdate 方法执行“upsert”操作。 Seed因为 每次执行 update-database 命令时,方法都会运行,通常在每次迁移后,你不能只插入数据,因为尝试添加的行在创建数据库的第一次迁移之后已经存在。 “upsert”操作可防止在尝试插入已存在的行时发生的错误,但它 会替代 在测试应用程序时对数据所做的任何更改。 对于某些表中的测试数据,你可能不希望发生这种情况:在某些情况下,在测试时更改数据时,希望更改在数据库更新后保留。 在这种情况下,需要执行条件插入操作:仅当行尚不存在时才插入该行。 Seed 方法使用这两种方法。

    传递给 AddOrUpdate 方法的第一个参数指定要用于检查行(如果已存在)的属性。 对于你提供的测试学生数据,属性可用于此目的, LastName 因为列表中的每个姓氏都是唯一的:

    context.Students.AddOrUpdate(p => p.LastName, s)
    

    此代码假定姓氏是唯一的。 如果手动添加姓氏重复的学生,下次执行迁移时会出现以下异常:

    序列包含多个元素

    有关如何处理冗余数据(如两个名为“Alexander Carson”的学生)的信息,请参阅 Rick Anderson 博客上的 种子设定和调试实体框架 (EF) DB 。 有关该方法的详细信息 AddOrUpdate ,请参阅 Julie Lerman 博客上的“ 小心使用 EF 4.3 AddOrUpdate 方法 ”。

    创建Enrollment实体的代码假定你在集合中的students实体中具有 ID 值,尽管未在创建集合的代码中设置该属性。

    new Enrollment { 
        StudentID = students.Single(s => s.LastName == "Alexander").ID, 
        CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID, 
        Grade = Grade.A 
    },
    

    可以在此处使用 ID 属性, ID 因为值是在为集合调用 SaveChanges 时设置的 students 。 EF 在将实体插入数据库时自动获取主键值,并在内存中更新 ID 实体的 属性。

    将每个 Enrollment 实体添加到 Enrollments 实体集的代码不使用 AddOrUpdate 方法。 它检查实体是否已存在,如果实体不存在,则插入该实体。 此方法将保留使用应用程序 UI 对注册等级所做的更改。 代码循环访问 List 的每个成员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);
        }
    }
    
  2. 生成项目。

执行第一次迁移

执行 add-migration 命令时,迁移会生成从头开始创建数据库的代码。 此代码也位于 Migrations 文件夹中,位于名为 <timestamp>_InitialCreate.cs 的文件中。 Up类的 InitialCreate 方法将创建与数据模型实体集对应的数据库表,并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");
    }
}

迁移调用 Up 方法为迁移实现数据模型更改。 输入用于回退更新的命令时,迁移调用 Down 方法。

这是输入 add-migration InitialCreate 命令时创建的初始迁移。 示例) 中的参数 (InitialCreate 用于文件名,并且可以是所需的任意参数;通常选择总结迁移中正在执行的操作的单词或短语。 例如,可将后面的迁移命名为“AddDepartmentTable”。

如果创建初始迁移时已存在数据库,则会生成数据库创建代码,但此代码不必运行,因为数据库已与数据库模型相匹配。 将应用部署到其中尚不存在数据库的其他环境时,此代码将运行以创建数据库,因此最好提前进行测试。 这就是你之前在连接字符串中更改数据库名称的原因,以便迁移可以从头开始创建新数据库。

  1. 在“程序包管理器控制台”窗口中,输入以下命令:

    update-database

    命令 update-database 运行 Up 方法以创建数据库,然后运行 Seed 方法来填充数据库。 部署应用程序后,相同的过程将在生产环境中自动运行,如以下部分所示。

  2. 使用 服务器资源管理器 检查数据库,就像在第一个教程中所做的那样,并运行应用程序以验证一切是否仍然像以前一样工作。

部署到 Azure

到目前为止,应用程序已在开发计算机上IIS Express本地运行。 若要使其可供其他人通过 Internet 使用,必须将其部署到 Web 托管提供商。 在本教程的本部分中,你将将其部署到 Azure。 此部分是可选的;可以跳过此教程并继续学习以下教程,也可以根据所选的其他托管提供商调整本部分中的说明。

使用 Code First 迁移部署数据库

若要部署数据库,请使用 Code First 迁移。 创建用于配置从 Visual Studio 进行部署的设置的发布配置文件时,你将选择标记为“更新数据库”的检查框。 此设置会导致部署过程在目标服务器上自动配置应用程序 Web.config 文件,以便 Code First 使用 MigrateDatabaseToLatestVersion 初始值设定项类。

Visual Studio 在将项目复制到目标服务器时,在部署过程中不会对数据库执行任何操作。 运行部署的应用程序并在部署后首次访问数据库时,Code First 会检查数据库是否与数据模型匹配。 如果不匹配,Code First 会自动创建数据库 (如果数据库尚不存在) ,或将数据库架构更新为最新版本 (如果数据库存在但与模型) 不匹配。 如果应用程序实现 Migrations Seed 方法,则该方法在创建数据库或更新架构后运行。

迁移 Seed 方法插入测试数据。 如果要部署到生产环境,则必须更改 方法, Seed 使其仅插入要插入生产数据库的数据。 例如,在当前数据模型中,你可能想要在开发数据库中设置真实课程,但要有虚构的学生。 可以编写方法 Seed 以在开发中加载两者,然后在部署到生产环境之前注释掉虚构的学生。 或者,可以编写方法 Seed 以仅加载课程,并使用应用程序的 UI 手动输入测试数据库中的虚构学生。

获取 Azure 帐户

你将需要一个 Azure 帐户。 如果你还没有订阅,但确实拥有 Visual Studio 订阅,则可以 激活订阅权益。 否则,只需几分钟即可创建免费试用帐户。 有关详细信息,请参阅 Azure 免费试用

在 Azure 中创建网站和 SQL 数据库

Azure 中的 Web 应用将在共享托管环境中运行,这意味着它在与其他 Azure 客户端共享的虚拟机 (VM) 上运行。 共享宿主环境是一种在云中开始工作的低成本方式。 稍后,如果你的 Web 流量增加,则应用程序可进行扩展,通过在专用 VM 上运行来满足需要。 若要详细了解Azure 应用服务的定价选项,请阅读App 服务定价

将数据库部署到Azure SQL数据库。 SQL 数据库是基于SQL Server技术构建的基于云的关系数据库服务。 使用 SQL Server 的工具和应用程序也适用于 SQL 数据库。

  1. Azure 管理门户中,选择左侧选项卡中的“创建资源”,然后在“新建”窗格 (或边栏选项卡) 上选择“查看全部”,查看所有可用资源。 在“一切”边栏选项卡的“Web”部分选择“Web 应用 + SQL”。 最后,选择“ 创建”。

    在 Azure 门户 中创建资源

    此时会打开用于创建新的 Web 应用 + SQL 资源的窗体。

  2. “应用名称 ”框中输入一个字符串,用作应用程序的唯一 URL。 完整的 URL 将包含在此处输入的内容以及 Azure 应用 Services (.azurewebsites.net) 的默认域。 如果已获取 应用名称 ,向导会通知你一条红色 的“应用名称不可用 ”消息。 如果 应用名称 可用,则会看到绿色复选标记。

  3. 在“订阅”框中,选择希望App 服务驻留的 Azure 订阅。

  4. 在“ 资源组 ”文本框中,选择资源组或创建新资源组。 此设置指定您的网站将在哪个数据中心运行。 有关资源组的详细信息,请参阅 资源组

  5. 单击App 服务“新建部分创建新的App 服务计划,然后填写App 服务计划, (可以与App 服务) 、位置和定价层同名, (有一个免费选项) 。

  6. 单击“SQL 数据库”,然后选择“创建新数据库”或选择现有数据库。

  7. 在“ 名称 ”框中,输入数据库的名称。

  8. 单击“ 目标服务器 ”框,然后选择“ 创建新服务器”。 或者,如果以前创建了服务器,则可以从可用服务器列表中选择该服务器。

  9. 选择 “定价层” 部分,选择“ 免费”。 如果需要其他资源,可以随时纵向扩展数据库。 若要详细了解Azure SQL定价,请参阅Azure SQL数据库定价

  10. 根据需要修改 排序规则

  11. 输入管理员 SQL 管理员用户名SQL 管理员密码

    • 如果选择了“新建SQL 数据库服务器”,请定义稍后访问数据库时要使用的新名称和密码。
    • 如果选择了之前创建的服务器,请输入该服务器的凭据。
  12. 可以使用 Application Insights 为App 服务启用遥测收集。 Application Insights 几乎无需配置,就会收集有价值的事件、异常、依赖项、请求和跟踪信息。 若要详细了解 Application Insights,请参阅 Azure Monitor

  13. 单击底部的“ 创建 ”以指示已完成。

    管理门户将返回到“仪表板”页,页面顶部的 “通知 ”区域显示正在创建网站。 (一段时间后,通常不到一分钟) ,会出现一条通知,指出部署成功。 在左侧导航栏中,新的App 服务显示在“应用服务”部分中,新的 SQL 数据库显示在“SQL 数据库”部分中。

将应用部署到 Azure

  1. 在 Visual Studio 中,在“解决方案资源管理器”中右键单击项目,并从上下文菜单中选择“发布”。

  2. “选择发布目标”页上,选择“App 服务”,然后选择“选择现有”,然后选择“发布”。

    选择发布目标页面

  3. 如果之前未在 Visual Studio 中添加 Azure 订阅,请执行屏幕上的步骤。 这些步骤使 Visual Studio 能够连接到 Azure 订阅,以便 应用服务 列表将包含网站。

  4. “App 服务”页上,选择App 服务添加到的订阅。 在“ 视图”下,选择“ 资源组”。 展开App 服务添加到的资源组,然后选择App 服务。 选择 “确定” 以发布应用。

  5. “输出”窗口会显示已执行的部署操作并报告部署的成功完成。

  6. 成功完成部署后,默认浏览器会自动打开并指向已部署网站的 URL。

    Students_index_page_with_paging

    你的应用现在在云中运行。

此时,已在 Azure SQL 数据库中创建了 SchoolContext 数据库,因为你选择了“执行Code First 迁移 (在应用启动) 上运行。 已部署网站中的 Web.config 文件已更改,以便当代码首次读取或写入数据库中的数据时, MigrateDatabaseToLatestVersion 初始值设定项将运行 (在选择“ 学生 ”选项卡时) :

Web.config文件摘录

部署过程还创建了一个新的连接字符串 (SchoolContext_DatabasePublish) ,供Code First 迁移用于更新数据库架构和设定数据库的种子。

Web.config 文件中的连接字符串

可以在自己的计算机上找到已部署版本的 Web.config 文件 ContosoUniversity\obj\Release\Package\PackageTmp\Web.config。可以使用 FTP 访问部署 Web.config 文件本身。 有关说明,请参阅 使用 Visual Studio ASP.NET Web 部署:部署代码更新。 按照以“若要使用 FTP 工具,需要三个内容”开头的说明:FTP URL、用户名和密码。

注意

Web 应用不实现安全性,因此找到 URL 的任何人都可以更改数据。 有关如何保护网站的说明,请参阅 将具有成员资格、OAuth 和 SQL 数据库的安全 ASP.NET MVC 应用部署到 Azure。 可以通过在 Visual Studio 中使用 Azure 管理门户或 服务器资源管理器 停止服务来阻止其他人使用站点。

停止应用服务菜单项

高级迁移方案

如果按照本教程中所示,通过自动运行迁移来部署数据库,并且要部署到在多个服务器上运行的网站,则可以让多个服务器同时尝试运行迁移。 迁移是原子性的,因此,如果两个服务器尝试运行同一个迁移,一个服务器将成功,另一个将失败 (假设操作不能) 两次。 在这种情况下,如果想要避免这些问题,可以手动调用迁移并设置自己的代码,使其仅发生一次。 有关详细信息,请参阅 Rowan Miller 博客上的 从代码运行迁移和编写脚本 ,以及用于从命令行 ) 执行迁移的Migrate.exe(。

有关其他迁移方案的信息,请参阅 迁移截屏视频系列

更新特定迁移

update-database -target MigrationName

命令 update-database -target MigrationName 运行目标迁移。

忽略对数据库的迁移更改

Add-migration MigrationName -ignoreChanges

ignoreChanges使用当前模型作为快照创建空迁移。

Code First 初始值设定项

在部署部分中,你看到了正在使用 MigrateDatabaseToLatestVersion 初始值设定项。 Code First 还提供其他初始值设定项,包括 createDatabaseIfNotExists (默认) 、 DropCreateDatabaseIfModelChanges ((之前) )和 DropCreateDatabaseAlways。 初始 DropCreateAlways 值设定项可用于设置单元测试的条件。 还可以编写自己的初始值设定项,如果不想等到应用程序从数据库读取或写入数据库,则可以显式调用初始值设定项。

有关初始值设定项的详细信息,请参阅 了解实体框架代码中的数据库初始值设定项, 以及 Julie Lerman 和 Rowan Miller 编写 实体框架编程:代码优先 一书的第 6 章。

获取代码

下载已完成的项目

其他资源

可以在 ASP.NET 数据访问 - 推荐资源中找到指向其他实体框架资源的链接。

后续步骤

在本教程中,你将了解:

  • 已启用 Code First 迁移
  • 在 Azure 中部署应用 (可选)

请转到下一篇文章,了解如何为 ASP.NET MVC 应用程序创建更复杂的数据模型。