EF6与MVC5系列(5):使用数据库迁移( Code First Migrations)和发布

本节教程包含以下内容:

  • 启用数据库迁移(Code First Migrations):迁移特性可以改变数据模型,并且不需要删除重建数据库就可以修改数据库架构。
  • 部署在Azure中:这步骤是可选的,可以不发布在Azure中继续学习本教程后面的内容。

ps:本文中只翻译Code First Migrations相关内容,有关如何在Azure中发布,可以查看原文:https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/migrations-and-deployment-with-the-entity-framework-in-an-asp-net-mvc-application

启用Code First Migrations

在开发过程中,数据模型会频繁的变动,每次数据模型的修改都会导致数据模型与数据库不同步。我们已经配置了EF,这样在每次修改数据库模型以后EF会自动删除重建数据库。当你添加,移除,修改实体类或者修改DbContext类,程序会自动删除已存在的数据库,创建与数据模型相匹配的数据库用于保存测试数据。

这种与数据库同步的方法在你发布网站之前是可以的,当实际运行中,程序中存储的都是我们想要的数据,而且我们不希望因为数据模型的变动导致这些数据丢失。 Code First Migrations特性通过启用 Code First 修改数据库架构解决了这个问题。而不需要删除重建数据库。这节中,我们将发布程序,并开始启用数据库迁移。

1.注释掉之前在Web.Config文件中添加的 contexts节点。

<entityFramework>
    <!--<contexts>
      <context type="ContosoUniversity.DAL.SchoolContext,ContosoUniversity">
        <databaseInitializer type="ContosoUniversity.DAL.SchoolInitailizer,ContosoUniversity" />
      </context>
    </contexts>-->
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="mssqllocaldb" />
      </parameters>
    </defaultConnectionFactory>

2.同时修改连接字符串中的数据库名字为:ContosoUniversity2

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

这样第一次数据库迁移会创建一个新的数据库,不是必须这么做,但是这样比较好。

3.在vs中,工具->NuGet库程序包管理器->程序包管理器控制台

4.在控制台中键入以下命令:

enable-migrations
add-migration InitialCreate

enable-migrations命令在项目中创建了Migrations文件夹,并且在文件夹中有一个Configuration.cs文件可以用来配置迁移。

(如果在上述过程中,你省略了改变数据库名称这一步骤,数据库迁移会查找到已经存在的数据库,并自动添加add-migration命令,不过没关系,这样只不过会在你部署数据库之前不会运行测试的迁移代码。稍后运行update-database命令的时候,不会发生有任何变化,因为数据库已经存在了)

和你看到之前的初始化数据库类是一样的。Configuration类中包含一个Seed方法。这个方法是当你创建或者修改数据库之后可以插入或者修改数据。创建数据库以后会调用这个方法,每次数据模型改变导致数据库结构改变也会调用此方法。

修改Seed方法

删除重建数据库的时候使用初始化类的Seed方法添加测试数据,因为每次模型变动会导致数据库数据丢失。但使用了数据库迁移技术,原来的数据也会保留在数据库中,所以 在Seed方法中包含测试数据不是必须的。实际上真正项目部署的时候,我们并不希望Seed方法添加测试数据,而是添加我们需要的真实数据。

这节中,发布的时候我们将使用迁移,但是无论如何Seed方法都是添加测试数据,为了便于我们查看功能,使我们不需要手动添加大量数据。

1。修改Configuration.cs文件,下面代码将会在新的数据库中加载测试数据。

  1 namespace ContosoUniversity.Migrations
  2 {
  3     using System;
  4     using System.Data.Entity;
  5     using System.Data.Entity.Migrations;
  6     using System.Linq;
  7     using ContosoUniversity.Models;
  8     using System.Collections.Generic;
  9
 10     internal sealed class Configuration : DbMigrationsConfiguration<ContosoUniversity.DAL.SchoolContext>
 11     {
 12         public Configuration()
 13         {
 14             AutomaticMigrationsEnabled = false;
 15         }
 16
 17         protected override void Seed(ContosoUniversity.DAL.SchoolContext context)
 18         {
 19             var students = new List<Student>{
 20 new Student { FirstMidName = "Carson",   LastName = "Alexander",
 21                     EnrollmentDate = DateTime.Parse("2010-09-01") },
 22                 new Student { FirstMidName = "Meredith", LastName = "Alonso",
 23                     EnrollmentDate = DateTime.Parse("2012-09-01") },
 24                 new Student { FirstMidName = "Arturo",   LastName = "Anand",
 25                     EnrollmentDate = DateTime.Parse("2013-09-01") },
 26                 new Student { FirstMidName = "Gytis",    LastName = "Barzdukas",
 27                     EnrollmentDate = DateTime.Parse("2012-09-01") },
 28                 new Student { FirstMidName = "Yan",      LastName = "Li",
 29                     EnrollmentDate = DateTime.Parse("2012-09-01") },
 30                 new Student { FirstMidName = "Peggy",    LastName = "Justice",
 31                     EnrollmentDate = DateTime.Parse("2011-09-01") },
 32                 new Student { FirstMidName = "Laura",    LastName = "Norman",
 33                     EnrollmentDate = DateTime.Parse("2013-09-01") },
 34                 new Student { FirstMidName = "Nino",     LastName = "Olivetto",
 35                     EnrollmentDate = DateTime.Parse("2005-08-11") }           };
 36             students.ForEach(s => context.Students.AddOrUpdate(p => p.LastName, s));
 37             context.SaveChanges();
 38
 39             var courses = new List<Course> {
 40             new Course {CourseID = 1050, Title = "Chemistry",      Credits = 3, },
 41                 new Course {CourseID = 4022, Title = "Microeconomics", Credits = 3, },
 42                 new Course {CourseID = 4041, Title = "Macroeconomics", Credits = 3, },
 43                 new Course {CourseID = 1045, Title = "Calculus",       Credits = 4, },
 44                 new Course {CourseID = 3141, Title = "Trigonometry",   Credits = 4, },
 45                 new Course {CourseID = 2021, Title = "Composition",    Credits = 3, },
 46                 new Course {CourseID = 2042, Title = "Literature",     Credits = 4, }
 47             };
 48             courses.ForEach(c=>context.Courses.AddOrUpdate(p=>p.Title,c));
 49             context.SaveChanges();
 50
 51             var enrollments = new List<Enrollment>{
 52                 new Enrollment {
 53                     StudentID = students.Single(s => s.LastName == "Alexander").ID,
 54                     CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID,
 55                     Grade = Grade.A
 56                 },
 57                  new Enrollment {
 58                     StudentID = students.Single(s => s.LastName == "Alexander").ID,
 59                     CourseID = courses.Single(c => c.Title == "Microeconomics" ).CourseID,
 60                     Grade = Grade.C
 61                  },
 62                  new Enrollment {
 63                     StudentID = students.Single(s => s.LastName == "Alexander").ID,
 64                     CourseID = courses.Single(c => c.Title == "Macroeconomics" ).CourseID,
 65                     Grade = Grade.B
 66                  },
 67                  new Enrollment {
 68                      StudentID = students.Single(s => s.LastName == "Alonso").ID,
 69                     CourseID = courses.Single(c => c.Title == "Calculus" ).CourseID,
 70                     Grade = Grade.B
 71                  },
 72                  new Enrollment {
 73                      StudentID = students.Single(s => s.LastName == "Alonso").ID,
 74                     CourseID = courses.Single(c => c.Title == "Trigonometry" ).CourseID,
 75                     Grade = Grade.B
 76                  },
 77                  new Enrollment {
 78                     StudentID = students.Single(s => s.LastName == "Alonso").ID,
 79                     CourseID = courses.Single(c => c.Title == "Composition" ).CourseID,
 80                     Grade = Grade.B
 81                  },
 82                  new Enrollment {
 83                     StudentID = students.Single(s => s.LastName == "Anand").ID,
 84                     CourseID = courses.Single(c => c.Title == "Chemistry" ).CourseID
 85                  },
 86                  new Enrollment {
 87                     StudentID = students.Single(s => s.LastName == "Anand").ID,
 88                     CourseID = courses.Single(c => c.Title == "Microeconomics").CourseID,
 89                     Grade = Grade.B
 90                  },
 91                 new Enrollment {
 92                     StudentID = students.Single(s => s.LastName == "Barzdukas").ID,
 93                     CourseID = courses.Single(c => c.Title == "Chemistry").CourseID,
 94                     Grade = Grade.B
 95                  },
 96                  new Enrollment {
 97                     StudentID = students.Single(s => s.LastName == "Li").ID,
 98                     CourseID = courses.Single(c => c.Title == "Composition").CourseID,
 99                     Grade = Grade.B
100                  },
101                  new Enrollment {
102                     StudentID = students.Single(s => s.LastName == "Justice").ID,
103                     CourseID = courses.Single(c => c.Title == "Literature").CourseID,
104                     Grade = Grade.B
105                  }
106             };
107             foreach(Enrollment e in enrollments)
108             {
109                 var enrollmentInDatabase = context.Enrollments.Where(s=>
110                     s.Student.ID==e.StudentID &&
111                     s.Course.CourseID==e.CourseID).SingleOrDefault();
112                 if (enrollmentInDatabase ==null)
113                 {
114                     context.Enrollments.Add(e);
115                 }
116             }
117             context.SaveChanges();
118         }
119     }
120 }

Seed方法将context作为传入参数,并使用context在数据库中添加新数据。对于每种实体类型,创建新的实体集合,并将这些集合添加到相应的Dbset中,然后保存对数据库做的更改。没必要像上面代码如此,在每组实体后面都添加SaveChanges方法。此处这么做是为了当向数据库中添加实体的时候如果抛出异常便于我们找到问题源头。

添加数据的时候使用了AddOrUpdate方法。每次数据迁移之后调用执行 update-database命令都会运行Seed方法,但这样不仅仅是插入数据,因为创建数据库第一次数据迁移以后有些数据已经存在。更新插入的操作可以防止当你向数据库中添加已存在的数据时报错,但它会覆盖所有数据修改。有时我们不希望这种事情发生:比如我们测试的时候修改了数据,但是我们仍希望数据库修改以后,这些数据仍然保留。这时候我们用条件插入操作:只有在数据库中不存在该记录的时候才添加数据。

AddOrUpdate 方法的第一个参数指定了使用某个属性去检查数据行是否存在。对于你正在提供的Student测试数据,LastName属性可以作为判断数据库中是否存在该数据的判断依据。因为列表中的每个LastName属性都是唯一的。

上述代码指定了LastName是唯一的,如果LastName不是唯一的。在下一次执行数据库迁移的时候会出现以下异常。

Sequence contains more than one element

对如何处理冗余数据,比如数据库中有两个名为:Alexander Carson的学生。请查看:Seeding and Debugging Entity Framework (EF) DBs 。有关更多AddOrUpdate方法请查看: Take care with EF 4.3 AddOrUpdate Method

代码创建了Enrollment实体,假设在student实体集中有ID值,尽管你并没有在创建集合的代码中设置这个属性。

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

在此处可以使用ID属性,是因为对Student调用了SaveChanges 方法的时候设置了ID属性。当将实体添加到数据库中的时候,EF会自动获取到主键值,并且修改内存中的ID属性。

Enrollment实体添加到Enrollments 实体集并没有使用AddOrUpdate方法。它检查了实体集中是否存在该实体,如果没有,就添加进去。这样我们通过运行程序创建新的入学信息到数据库,迁移的时候,创建的新信息将会保存在数据库。循环遍历Enrollment中的每个成员。如果数据库中不存在就添加该成员。第一次修改数据库的时数据库是空的,所以每一个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文件夹,里面包含一个<时间戳>_InitialCreate.cs文件。<时间戳>_InitialCreate.cs文件中的Up方法,会创建与数据模型实体相匹配的数据库,而里面的Down方法会删除数据库中的表。

迁移会调用UP方法实现数据模型的更改,当我们输入回滚修改的命令时,迁移会调用Down方法。

这些都是你输入add-migration InitialCreate命令时候初始化迁移所创建的,命令中的InitialCreate只是一个参数,它是任意命名的。

当数据库已经存在的时候你创建了初始化迁移,尽管数据库创建代码生成了但是它并不会运行,因为数据库已经和数据模型相匹配。当你部署到另外的没有数据库存在的环境中,代码才会运行并创建数据库。因此最好先测试下。这就是为什么先前修改连接字符串中的数据库名称。这样迁移可以创建一个新的数据库。

1.在程序包管理器控制台中输入以下命令:update-database

这个命令运行了InitialCreate中的up方法创建了数据库,运行Seed方法将数据填充到数据库中。当你部署网站的时候,也会发生同样的处理过程,稍后章节中将会看到。

2.在服务器资源管理器中查看你创建的数据库,如何查看已经在教程的第一章中讲到。运行程序看看是否和之前运行的一样。

使用数据库迁移部署数据库

原文请查看:https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/migrations-and-deployment-with-the-entity-framework-in-an-asp-net-mvc-application

时间: 2024-10-16 22:21:59

EF6与MVC5系列(5):使用数据库迁移( Code First Migrations)和发布的相关文章

EF6与mvc5系列(1)开始

本系列教程是微软asp.net网站上的教程翻译,原文地址:http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application . 我们将用EF6和mvc5技术完成一个应用程序Contoso University.这是一个虚拟的项目.里面包含学生管理,成绩管理,课

EF6与mvc5系列(1)

本系列教程是微软asp.net网站上的教程翻译,原文地址:http://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application   欢迎大家批评指正,共同进步. 开发工具: vs2013 .NET4.5 EF6(有关ef的版本,根据项目安装版本而定) 创建MVC应

EF6与mvc5系列(3):在MVC应用程序中使用EF进行排序,过滤和分页

上节中,我们实现了基本增删查改功能,本节中要在Student的Index页面添加排序,分页和过滤功能,同时创建一个简单的分组页面. 在Student的Index页面添加列排序链接 为了在Index页面中实现排序.修改Index方法中的代码. 在Index方法中添加排序功能 修改Student控制器中的Index方法,在Index视图中添加代码. // GET: /Student/ public ActionResult Index(string sortOrder) { ViewBag.Name

EF6与mvc5系列(2):实现基本的增删查改

在上节中添加控制器以后,项目中自动生成了增删改查视图.本节中我们将对其进行修改. 创建详情页 虽然项目中有Detail视图,但是我们无法查看学生的选课信息.所以修改Detail视图页如下,添加Enrollments信息. 1 @model ContosoUniversity.Models.Student 2 3 @{ 4 ViewBag.Title = "Details"; 5 } 6 7 <h2>Details</h2> 8 9 <div> 10

EntityFramework CodeFirst 数据库迁移

参考: https://msdn.microsoft.com/en-us/data/jj591621 http://www.itnose.net/detail/6105449.html http://www.tuicool.com/articles/Q7JRR32 打开:工具 --> NuGet包管理器 --> 程序包管理控制台,按下面的步骤使用相应的命令 //使能迁移功能 命令1. Enable-Migrations -ContextTypeName WebTest.Models.TestD

EF6.0+APS.NET MVC5.0项目初探三(code first实体映射到数据库)

到这里架构就搭建完了,该向里面填充东西的时候了,如上篇:EF6.0+APS.NET MVC5.0项目初探二(类库引用关系及说明) 第一步 :在需要添加EF的类库Domain.DbContext上右击->管理NuGet程序包->找到Entity FrameWork下载安装. 如图: 第二步:新建DbContext 第三步:在类库Domain.Entity上添加引用System.ComponentModel.DataAnnotations(用于验证的引用) 并新建实体类. 1 using Syst

EF6 学习笔记(五):数据库迁移及部署

EF6学习笔记总目录:ASP.NET MVC5 及 EF6 学习笔记 - (目录整理) 原文地址:Code First Migrations and Deployment 原文主要讲两部分:开发环境下数据库迁移到其他数据库服务器:以及在Azure上如何部署应用: 原文前面讲一堆内容,主要就是说数据库在开发过程中,如果数据模型经常需要调整,那么数据库每次都删除重建有点不太现实:尤其是对于已部署的正式环境,需要的仅仅是数据库升级,而不可能直接Drop掉,再重新Create. 所以,需要启用EF的数据

MVC5+EF6简单实例---以原有SQLServer数据库两表联合查询为例

工具:VS.net2013.EF6.MVC5.SQLServer2008 参考出处: http://www.cnblogs.com/slark/p/mvc-5-get-started-create-project.html http://www.cnblogs.com/miro/p/4288184.html http://www.cnblogs.com/dotnetmvc/p/3732029.html 一.准备工作 在SqlServer上创建数据库:Element 模拟两个表并插入数据:SysU

20.翻译系列:Code-First中的数据库迁移技术【EF 6 Code-First系列】

原文链接:https://www.entityframeworktutorial.net/code-first/migration-in-code-first.aspx Entity Framework Code-First有很多不同的数据库初始化策略,例如:CreateDatabaseIfNotExists[创建数据库,如果不存在的话].DropCreateDatabaseIfModelChanges[如果模型发生改变的话,就删除重建数据库].DropCreateDatabaseAlways[