最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来。

十年河东十年河西,莫欺少年穷

学无止境,精益求精

   标题叫EF CodeFirs 代码迁移、数据迁移。

那么:到底叫代码迁移还是数据迁移?我在网上看了大半天,怎么叫的都有,后来查了MSDN,MSDN上叫代码迁移。在此,我们也称之为代码迁移。

为什么有人将其称为数据迁移呢?可能是因为本节内容和操作数据库有关<增加一张表,删除一张表,增加一个表字段,删除一个表字段,修改一个表字段等>,所以网友称之为数据迁移

MSDN等权威结构为什么称之为代码迁移呢?可能是因为本节的内容通过我们熟知的C#代码就可以搞定,所以称之为代码迁移

嘻嘻,上边是我的猜测,人家可跳过

正文:

背景:我们为什么要使用代码迁移?

我们使用CodeFirst初始化数据库无非以下三种方式:

一、 每次运行都会创建新的数据库

Database.SetInitializer<XXXXXContext>(new DropCreateDatabaseAlways<XXXXXContext>());

二、只有第一次运行~才会创建新的数据库~默认的方式

Database.SetInitializer<XXXXXContext>(new CreateDatabaseIfNotExists<XXXXXContext>());

三、 修改模型后~运行~会创建新的数据库

Database.SetInitializer<XXXXXContext>(new DropCreateDatabaseIfModelChanges<XXXXXContext>());

不论我们使用哪种方式,当条件成立时,都会创建新的数据库,而创建新的数据库就意味着‘从头再来’

举个例子:如果您的团队开发的项目已经被客户使用,客户在实际的生产操作中已经创建了很多数据,如果,某一天客户更改需求,我们难不成要采用上述方式将数据库重建?一旦重建,客户之前的数据就会丢失,这是任何一个客户不能容忍的

为了解决这个问题,我们要引入代码迁移

在进行代码迁移之前,我们必须做好数据库备份工作,确保客户数据的安全性及完整性

现在我们引入数据迁移示例:

我们有以下模型类:

Student模型类

  1. public class Student
  2. {
  3. [Key]
  4. public int Id { get; set; }
  5. [Required]
  6. [StringLength()]
  7. public string Name { get; set; }//姓名
  8. [StringLength()]
  9. public string Sex { get; set; }//性别
  10. [Unique(ErrorMessage="学号不允许重复")]
  11. [StringLength()]
  12. public string StudentNum { get; set; }//学号
  13.  
  14. [StringLength()]
  15. public string StudentAddress { get; set; }//地址
  16. }

Course模型类

  1. public class Course
  2. {
  3. [Key]
  4. public int Id { get; set; }
  5. [Required]
  6. [StringLength()]
  7. public string Name { get; set; }//课程名称
    }

Score模型类

  1. [Key]
  2. public int Id { get; set; }
  3.  
  4. public int StudentScore { get; set; }//学生分数
  5.  
  6. public int StudentID { get; set; }//学生ID
  7.  
  8. public int CourseID { get; set; }//课程ID
  9.  
  10. public virtual Student Student { get; set; }//virtual关键字修饰,用于延迟加载 提高性能 只有显式调用时 才会加载 并可以代表一个Student对象 也就是 属性==对象
  11.  
  12. public virtual Course Course { get; set; }//virtual关键字修饰,用于延迟加载 提高性能 只有显式调用时 才会加载 并可以代表一个Course对象 也就是 属性==对象

StudentContext上下文

  1. public class StudentContext : DbContext
  2. {
  3. public StudentContext()
  4. : base("StudentContext")//指定连接字符串
  5. {
  6.  
  7. }
  8. public DbSet<Student> Students { get; set; }
  9. public DbSet<Course> Courses { get; set; }
  10. public DbSet<Score> Scores { get; set; }
  11. /// <summary>
  12. /// OnModelCreating方法中的modelBuilder.Conventions.Remove语句禁止表名称正在多元化。如果你不这样做,所生成的表将命名为Students、Courses和Enrollments。相反,表名称将是Student、Course和Enrollment。开发商不同意关于表名称应该多数。本教程使用的是单数形式,但重要的一点是,您可以选择哪个你更喜欢通过包括或省略这行代码的形式。
  13. /// </summary>
  14. /// <param name="modelBuilder"></param>
  15. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  16. {
  17. modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
  18. }
  19. }

现在客户提出如下需求:在确保数据完整性的情况下,添加一张学校表<字段:学校名称、学校地址,学校热线>及增加学生联系方式字段

首先,我们添加学校模型类:

  1. public class School//新建一张学校表-----------------------------新建的表
  2. {
  3. [Key]
  4. public int schoolId { get; set; }
  5. [Required]
  6. [StringLength()]
  7. public string schoolName { get; set; }//学校名称
  8.  
  9. [StringLength()]
  10. public string StudentAddress { get; set; }//学校地址
  11.  
  12. [StringLength()]
  13. public string StudentTel { get; set; }//学校热线
  14. }

修改学生模型类如下:

  1. public class Student
  2. {
  3. [Key]
  4. public int Id { get; set; }
  5. [Required]
  6. [StringLength()]
  7. public string Name { get; set; }//姓名
  8. [StringLength()]
  9. public string Sex { get; set; }//性别
  10. [Unique(ErrorMessage="学号不允许重复")]
  11. [StringLength()]
  12. public string StudentNum { get; set; }//学号
  13.  
  14. [StringLength()]
  15. public string StudentAddress { get; set; }//地址
  16.  
  17. [StringLength()]
  18. public string StudentTel { get; set; }//联系电话----------------------------新加的字段
  19. }

上下文类加上DBset<School>如下:

  1. public class StudentContext : DbContext
  2. {
  3. public StudentContext()
  4. : base("StudentContext")//指定连接字符串
  5. {
  6.  
  7. }
  8. public DbSet<Student> Students { get; set; }
  9. public DbSet<Course> Courses { get; set; }
  10. public DbSet<Score> Scores { get; set; }
  11. public DbSet<School> Schools { get; set; }//新加一张表
  12. /// <summary>
  13. /// OnModelCreating方法中的modelBuilder.Conventions.Remove语句禁止表名称正在多元化。如果你不这样做,所生成的表将命名为Students、Courses和Enrollments。相反,表名称将是Student、Course和Enrollment。开发商不同意关于表名称应该多数。本教程使用的是单数形式,但重要的一点是,您可以选择哪个你更喜欢通过包括或省略这行代码的形式。
  14. /// </summary>
  15. /// <param name="modelBuilder"></param>
  16. protected override void OnModelCreating(DbModelBuilder modelBuilder)
  17. {
  18. modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
  19. }
  20. }

下面,我们进行代码迁移<在进行代码迁移之前,您必须重新生成您的项目>:

首先,打开程序包管理器控制台

PM>提示符下输入以下命令︰

 enable-migrations

 add-migration InitialCreate

解释下这两个命令的含义:enable-migrations 是指启动迁移,此命令输入后,系统会检查上下文目标  add-migration InitialCreate 创建名称为:时间戳_InitialCreate.CS的文件,其中时间戳是根据当前时间点生成的。

我们得到如下信息

至此,我们在项目中会发现多出一个名为:Migrations的文件夹,文件夹内有两个文件:201612080727320_InitialCreate.cs 和 Configuration.cs

下面介绍下这两个文件的作用:

Configuration.cs 中有个Seed方法,您每次代码迁移执行时,都会执行seed()方法,您可以通过Seed方法增加一些必要的数据。例如,上述我们增加了一张学校模型类,在代码迁移时,我们希望插入一条学校数据,我们可以这样修改seed()方法:

下面是我修改后的Seed方法:

  1. namespace EF_Test.Migrations
  2. {
  3. using System;
  4. using System.Data.Entity;
  5. using System.Data.Entity.Migrations;
  6. using System.Linq;
  7. using EF_Test.DAL;
  8.  
  9. internal sealed class Configuration : DbMigrationsConfiguration<EF_Test.DAL.StudentContext>
  10. {
  11. public Configuration()
  12. {
  13. AutomaticMigrationsEnabled = false;
  14. }
  15.  
  16. protected override void Seed(EF_Test.DAL.StudentContext context)
  17. {
  18. context.Schools.AddOrUpdate(
  19. p => p.schoolId,
  20. new School { schoolName = "河南城建学院", StudentAddress = "平顶山市新华区" , StudentTel="0375-88888888" }
  21. );
  22. context.SaveChanges();
  23. }
  24. }
  25. }

201612080727320_InitialCreate.cs 或者称为:时间戳_InitialCreate.cs 是干嘛用的呢?我们来看看生成的代码,如下:

  1. namespace EF_Test.Migrations
  2. {
  3. using System;
  4. using System.Data.Entity.Migrations;
  5.  
  6. public partial class InitialCreate : DbMigration
  7. {
  8. public override void Up()
  9. {
  10. CreateTable(
  11. "dbo.Course",
  12. c => new
  13. {
  14. Id = c.Int(nullable: false, identity: true),
  15. Name = c.String(nullable: false, maxLength: ),
  16. })
  17. .PrimaryKey(t => t.Id);
  18.  
  19. CreateTable(
  20. "dbo.Score",
  21. c => new
  22. {
  23. Id = c.Int(nullable: false, identity: true),
  24. StudentScore = c.Int(nullable: false),
  25. StudentID = c.Int(nullable: false),
  26. CourseID = c.Int(nullable: false),
  27. })
  28. .PrimaryKey(t => t.Id)
  29. .ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
  30. .ForeignKey("dbo.Student", t => t.StudentID, cascadeDelete: true)
  31. .Index(t => t.StudentID)
  32. .Index(t => t.CourseID);
  33.  
  34. CreateTable(
  35. "dbo.Student",
  36. c => new
  37. {
  38. Id = c.Int(nullable: false, identity: true),
  39. Name = c.String(nullable: false, maxLength: ),
  40. Sex = c.String(maxLength: ),
  41. StudentNum = c.String(maxLength: ),
  42. StudentAddress = c.String(maxLength: ),
  43. StudentTel = c.String(maxLength: ),
  44. })
  45. .PrimaryKey(t => t.Id);
  46.  
  47. }
  48.  
  49. public override void Down()
  50. {
  51. DropForeignKey("dbo.Score", "StudentID", "dbo.Student");
  52. DropForeignKey("dbo.Score", "CourseID", "dbo.Course");
  53. DropIndex("dbo.Score", new[] { "CourseID" });
  54. DropIndex("dbo.Score", new[] { "StudentID" });
  55. DropTable("dbo.Student");
  56. DropTable("dbo.Score");
  57. DropTable("dbo.Course");
  58. }
  59. }
  60. }

有两个方法,Up()和Down() 都是些创建表,删除主键,删除表的一些动作。我们先不管这些,继续我们的代码迁移:

PM>提示符下输入以下命令︰

   Update-Database  或者  Update-Database -Verbose   前者仅仅是更新数据库,不会在程序包管理器控制台中输出执行的SQL语句,而后者则可以输出执行的SQL语句,我们姑且使用后者吧

执行失败了,原因也很明确,是因为我们现有的数据库中已经存在Course等表,再次创建会发生异常,怎么办?

我们翻开 201612080727320_InitialCreate.cs 这个文件,将此文件作如下修改:

  1. public override void Up()
  2. {
  3.  
  4. CreateTable(
  5. "dbo.School",
  6. c => new
  7. {
  8. schoolId = c.Int(nullable: false, identity: true),
  9. schoolName = c.String(nullable: false, maxLength: ),
  10. StudentAddress = c.String(maxLength: ),
  11. StudentTel = c.String(maxLength: ),
  12. })
  13. .PrimaryKey(t => t.schoolId);
  14.  
  15. }
  16.  
  17. public override void Down()
  18. {
  19. DropTable("dbo.School");
  20.  
  21. }

项目生成成功后,再次执行:Update-Database -Verbose

   

万事大吉,执行成功,而且执行了Seed方法,那么我们的数据库中应该有一张名为 School 的表,并且里面有一条数据,如下:

学校模型创建成功了,但是,学生表中添加的字段加上去了么?

根据上图,学生表结构并没有加上StudentTel一列,为何?我们该怎么办?

在此,我们执行:add-migration 命令   add-migration 命令和 add-migration InitialCreate 命令的不同是一个指定了后缀名称,一个没有指定,需要我们自定义!

根据上图,我们创建了一个后缀为:SshoolCreate的文件,如下:

双击文件,我们发现里面没什么内容,我们添加如下代码:

  1. public partial class SshoolCreate : DbMigration
  2. {
  3. public override void Up()
  4. {
  5. AddColumn("dbo.Student", "StudentTel", c => c.String(maxLength: ));
  6. }
  7.  
  8. public override void Down()
  9. {
  10. DropColumn("dbo.Student", "StudentTel");
  11. }
  12. }

继续执行:update-database 命令

执行成功,我们到数据库看看

字段StudentTel已经加入了表Student

至此,EF 代码迁移也就讲完了,上文比较乱,最后做个总结:

1、 数据库中有张名为:__MigrationHistory的表,主要用于代码迁移记录

每当你成功迁移一次,都会生成一条记录。

2、上文中提到:时间戳_InitialCreate.CS 文件,如果在项目中有多个同类文件,项目会执行距离当前时间最近的 时间戳_InitialCreate.CS,如果距离当前时间最近的 时间戳_InitialCreate.CS 在表__MigrationHistory中有记录,则不会执行!

3、其实代码迁移用到的命令也就那几个,在此总结下:

enable-migrations : 启动代码迁移并生成Configuration.CS文件

add-migration : 启动生成 时间戳_Name.CS文件,类似于:201612080802330_Name.CS这种文件,需要指定Name,用于多次迁移

add-migration InitialCreate  : 启动生成类似于:201612080802330_InitialCreate.CS这种文件

Update-Database -Verbose  和  Update-Database 执行代码迁移,修改数据库表结构。 前者会在执行过程中生成相应的SQL语句

4、常用的C#代码:

创建一张表:

  1. public partial class InitialCreate : DbMigration
  2. {
  3. public override void Up()
  4. {
  5. CreateTable(
  6. "dbo.School",
  7. c => new
  8. {
  9. schoolId = c.Int(nullable: false, identity: true),
  10. schoolName = c.String(nullable: false, maxLength: ),
  11. StudentAddress = c.String(maxLength: ),
  12. StudentTel = c.String(maxLength: ),
  13. })
  14. .PrimaryKey(t => t.schoolId);
  15. }
  16.  
  17. public override void Down()
  18. {
  19. DropTable("dbo.School");
  20.  
  21. }

删除一张表:

  1. public partial class DropTable : DbMigration
  2. {
  3. public override void Up()
  4. {
  5. DropTable("dbo.School");
  6. }
  7.  
  8. public override void Down()
  9. {
  10. CreateTable(
  11. "dbo.School",
  12. c => new
  13. {
  14. schoolId = c.Int(nullable: false, identity: true),
  15. schoolName = c.String(nullable: false, maxLength: ),
  16. StudentAddress = c.String(maxLength: ),
  17. StudentTel = c.String(maxLength: ),
  18. })
  19. .PrimaryKey(t => t.schoolId);
  20.  
  21. }
  22. }

修改字段长度或字段名称

  1. public partial class EEE : DbMigration
  2. {
  3. public override void Up()
  4. {
  5. AddColumn("dbo.Student", "StudentPhone", c => c.String(maxLength: ));
  6. DropColumn("dbo.Student", "StudentTel");
  7. }
  8.  
  9. public override void Down()
  10. {
  11. AddColumn("dbo.Student", "StudentTel", c => c.String(maxLength: ));
  12. DropColumn("dbo.Student", "StudentPhone");
  13. }
  14. }

增加表字段:

  1. public partial class FFF : DbMigration
  2. {
  3. public override void Up()
  4. {
  5. AddColumn("dbo.Student", "StudentPhowwwwne", c => c.String(maxLength: ));
  6. }
  7.  
  8. public override void Down()
  9. {
  10. DropColumn("dbo.Student", "StudentPhowwwwne");
  11. }
  12. }

删除表字段:

  1. public partial class GGG : DbMigration
  2. {
  3. public override void Up()
  4. {
  5. DropColumn("dbo.Student", "StudentPhowwwwne");
  6. }
  7.  
  8. public override void Down()
  9. {
  10. AddColumn("dbo.Student", "StudentPhowwwwne", c => c.String(maxLength: ));
  11. }
  12. }

等等吧,上述代码都能生成。

5、如果您生成了一个新的 时间戳_XXXX.CS文件,请务必使用Update-DataBase命令执行,否则,您将永远不会生成下一个 时间戳_XXXX.CS文件,究其原因,是因为在数据表[__MigrationHistory]中没有记录。

最新的[__MigrationHistory]数据

根据上述数据,就证明我做了五次代码迁移了!

好了,本节的内容也就讲完了,谢谢大家的耐心查阅!

@陈卧龙的博客

EF CodeFirs 代码迁移、数据迁移的更多相关文章

  1. 20.1翻译系列:EF 6中自动数据迁移技术【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/automated-migration-in-code-first.aspx EF 6 ...

  2. 一步一步学EF系列三【数据迁移】

    我们每篇的内容都不多,所以希望在学习的过程中最后能亲自敲一下代码 这样更有利于掌握. 我们现在接着上篇的例子,我们现在给随便的表增加一个字段 CreateTime 创建日期 运行一下 看看会怎么样 修 ...

  3. EF架构~CodeFirst数据迁移与防数据库删除

    回到目录 本文介绍两个概念,防数据库自动删除,这是由于在code first模式下,当数据实体发生变化时,会对原来数据库进行删除,并将新数据表添加进来,但这对于我们的运营环境数据库,是万万不能接受的, ...

  4. EF 中 Code First 的数据迁移以及创建视图

    写在前面: EF 中 Code First 的数据迁移网上有很多资料,我这份并没什么特别.Code First 创建视图网上也有很多资料,但好像很麻烦,而且亲测好像是无效的方法(可能是我太笨,没搞成功 ...

  5. EF Code First 数据迁移配置

    这里我想讲清楚code first 数据迁移的两种模式,还有开发环境和生产环境数据迁移的最佳实践. 1.1 数据迁移综述 EF Code first 虽然已经有了几种不同的数据库初始化策略,但是大部分 ...

  6. 20.2.翻译系列:EF 6中基于代码的数据库迁移技术【EF 6 Code-First系列】

    原文链接:https://www.entityframeworktutorial.net/code-first/code-based-migration-in-code-first.aspx EF 6 ...

  7. EF Code-First数据迁移

    Code-First数据迁移  首先要通过NuGet将EF升级至最新版本. 新建MVC 4项目MvcMigrationDemo 添加数据模型 Person 和 Department,定义如下: usi ...

  8. 【EF】EF Code-First数据迁移

    Code-First数据迁移  首先要通过NuGet将EF升级至最新版本. 新建MVC 4项目MvcMigrationDemo 添加数据模型 Person 和 Department,定义如下: usi ...

  9. 【odoo】[经验分享]数据迁移注意事项

    [odoo14]经典好书学习没有烂尾,主体已完成,可移步了解.https://www.cnblogs.com/xushuotec/p/14428210.html 背景 近期,有朋友打算上odoo系统. ...

随机推荐

  1. Linux 数组

    200 ? "200px" : this.width)!important;} --> :介绍 在shell4.0之后支持普通数组和关联数组,普通数组只能使用整数作为索引,关 ...

  2. Gradle与Gatling脚本集成

    Gatling作为次时代的性能测试工具,由于其API简洁明了.性能出众,越来越受欢迎.但是运行Gatling脚本却有诸多不便,其提供的默认方式不是很方便.考虑到Gatling脚本本质上是Scala类, ...

  3. git rm–r folder fatal:pathspec "" did not match any files

    问题描述: 某年某月某日,在查看git库的时候,发现文件的分布和文件夹的名字是极其不合理的,所以移动和重命名了某些文件. 在删除(git rm –r folder)一个空文件夹的时候,出现错误:fat ...

  4. wep.py输出hello world

    webpy是python的一个简单的web开发的框架.可以通过简单的几行代码启动一个web服务(虽然只是输出helloworld). 准备工作 准备工具如下: 下载python[python开发环境] ...

  5. windows下配置nginx+php环境

    刚看到nginx这个词,我很好奇它的读法(engine x),我的直译是"引擎x",一般引"擎代"表了性能,而"x"大多出现是表示" ...

  6. ECMAScript5中数组的方法

    1.forEach()方法 遍历数组,为每个数组元素调用指定函数,三个参数分别为:数组元素 item.元素索引 index.数组本身 arr,无返回值 例: 2.map()方法 调用数组的每个元素传递 ...

  7. salesforce 零基础开发入门学习(七)PickList的value值获取

    之前介绍过PickList类型的声明以及赋值,但是如何取出呢?一个sObject对象可以理解为一条数据.通过sObject直接取恐怕很难做到,因为他只会显示一个值.这时候就要用到Schema命名空间中 ...

  8. KnockoutJS 3.X API 第四章(14) 绑定语法细节

    data-bind绑定语法 Knockout的声明性绑定系统提供了一种简洁而强大的方法来将数据链接到UI. 绑定到简单的数据属性或使用单个绑定通常是容易和明显的. 对于更复杂的绑定,它有助于更好地了解 ...

  9. Android开发常用属性

    1.android string.xml 文字中间加入空格 android string.xml前后加空格的技巧 <string name="password">密   ...

  10. poj3342Party at Hali-Bula(树形dp)

    /* 树形dp! 判重思路: 当dp[v][0]==dp[v][1]时,很自然,flag[u][0]必然是有两种方案的.flag[u][1]则不然, 因为它只和dp[v][0]有关系.而若flag[v ...