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

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

学无止境,精益求精

   标题叫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模型类

    public class Student
{
[Key]
public int Id { get; set; }
[Required]
[StringLength()]
public string Name { get; set; }//姓名
[StringLength()]
public string Sex { get; set; }//性别
[Unique(ErrorMessage="学号不允许重复")]
[StringLength()]
public string StudentNum { get; set; }//学号 [StringLength()]
public string StudentAddress { get; set; }//地址
}

Course模型类

    public class Course
{
[Key]
public int Id { get; set; }
[Required]
[StringLength()]
public string Name { get; set; }//课程名称
}

Score模型类

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

StudentContext上下文

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

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

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

    public class School//新建一张学校表-----------------------------新建的表
{
[Key]
public int schoolId { get; set; }
[Required]
[StringLength()]
public string schoolName { get; set; }//学校名称 [StringLength()]
public string StudentAddress { get; set; }//学校地址 [StringLength()]
public string StudentTel { get; set; }//学校热线
}

修改学生模型类如下:

    public class Student
{
[Key]
public int Id { get; set; }
[Required]
[StringLength()]
public string Name { get; set; }//姓名
[StringLength()]
public string Sex { get; set; }//性别
[Unique(ErrorMessage="学号不允许重复")]
[StringLength()]
public string StudentNum { get; set; }//学号 [StringLength()]
public string StudentAddress { get; set; }//地址 [StringLength()]
public string StudentTel { get; set; }//联系电话----------------------------新加的字段
}

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

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

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

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

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方法:

namespace EF_Test.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using EF_Test.DAL; internal sealed class Configuration : DbMigrationsConfiguration<EF_Test.DAL.StudentContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
} protected override void Seed(EF_Test.DAL.StudentContext context)
{
context.Schools.AddOrUpdate(
p => p.schoolId,
new School { schoolName = "河南城建学院", StudentAddress = "平顶山市新华区" , StudentTel="0375-88888888" }
);
context.SaveChanges();
}
}
}

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

namespace EF_Test.Migrations
{
using System;
using System.Data.Entity.Migrations; public partial class InitialCreate : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.Course",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: ),
})
.PrimaryKey(t => t.Id); CreateTable(
"dbo.Score",
c => new
{
Id = c.Int(nullable: false, identity: true),
StudentScore = c.Int(nullable: false),
StudentID = c.Int(nullable: false),
CourseID = c.Int(nullable: false),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: true)
.ForeignKey("dbo.Student", t => t.StudentID, cascadeDelete: true)
.Index(t => t.StudentID)
.Index(t => t.CourseID); CreateTable(
"dbo.Student",
c => new
{
Id = c.Int(nullable: false, identity: true),
Name = c.String(nullable: false, maxLength: ),
Sex = c.String(maxLength: ),
StudentNum = c.String(maxLength: ),
StudentAddress = c.String(maxLength: ),
StudentTel = c.String(maxLength: ),
})
.PrimaryKey(t => t.Id); } public override void Down()
{
DropForeignKey("dbo.Score", "StudentID", "dbo.Student");
DropForeignKey("dbo.Score", "CourseID", "dbo.Course");
DropIndex("dbo.Score", new[] { "CourseID" });
DropIndex("dbo.Score", new[] { "StudentID" });
DropTable("dbo.Student");
DropTable("dbo.Score");
DropTable("dbo.Course");
}
}
}

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

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

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

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

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

        public override void Up()
{ CreateTable(
"dbo.School",
c => new
{
schoolId = c.Int(nullable: false, identity: true),
schoolName = c.String(nullable: false, maxLength: ),
StudentAddress = c.String(maxLength: ),
StudentTel = c.String(maxLength: ),
})
.PrimaryKey(t => t.schoolId); } public override void Down()
{
DropTable("dbo.School"); }

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

   

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

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

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

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

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

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

    public partial class SshoolCreate : DbMigration
{
public override void Up()
{
AddColumn("dbo.Student", "StudentTel", c => c.String(maxLength: ));
} public override void Down()
{
DropColumn("dbo.Student", "StudentTel");
}
}

继续执行: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#代码:

创建一张表:

    public partial class InitialCreate : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.School",
c => new
{
schoolId = c.Int(nullable: false, identity: true),
schoolName = c.String(nullable: false, maxLength: ),
StudentAddress = c.String(maxLength: ),
StudentTel = c.String(maxLength: ),
})
.PrimaryKey(t => t.schoolId);
} public override void Down()
{
DropTable("dbo.School"); }

删除一张表:

    public partial class DropTable : DbMigration
{
public override void Up()
{
DropTable("dbo.School");
} public override void Down()
{
CreateTable(
"dbo.School",
c => new
{
schoolId = c.Int(nullable: false, identity: true),
schoolName = c.String(nullable: false, maxLength: ),
StudentAddress = c.String(maxLength: ),
StudentTel = c.String(maxLength: ),
})
.PrimaryKey(t => t.schoolId); }
}

修改字段长度或字段名称

    public partial class EEE : DbMigration
{
public override void Up()
{
AddColumn("dbo.Student", "StudentPhone", c => c.String(maxLength: ));
DropColumn("dbo.Student", "StudentTel");
} public override void Down()
{
AddColumn("dbo.Student", "StudentTel", c => c.String(maxLength: ));
DropColumn("dbo.Student", "StudentPhone");
}
}

增加表字段:

    public partial class FFF : DbMigration
{
public override void Up()
{
AddColumn("dbo.Student", "StudentPhowwwwne", c => c.String(maxLength: ));
} public override void Down()
{
DropColumn("dbo.Student", "StudentPhowwwwne");
}
}

删除表字段:

    public partial class GGG : DbMigration
{
public override void Up()
{
DropColumn("dbo.Student", "StudentPhowwwwne");
} public override void Down()
{
AddColumn("dbo.Student", "StudentPhowwwwne", c => c.String(maxLength: ));
}
}

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

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. 一天一小段js代码(no.4)

    最近在看网上的前端笔试题,借鉴别人的自己来试一下: 题目: 写一段脚本,实现:当页面上任意一个链接被点击的时候,alert出这个链接在页面上的顺序号,如第一个链接则alert(1), 依次类推. 有一 ...

  2. [php入门] 5、初学CSS从中记下的一些基础点(For小白)

    CSS是层叠式样式表,主要用来控制页面的样式. 一.CSS概述 应用CSS: 1.外部样式表,CSS写在一个单独的.CSS文件中,在head里加<link rel="styleshee ...

  3. 据说每个大牛、小牛都应该有自己的库——DOM处理续

    在上篇据说每个大牛.小牛都应该有自己的库——DOM处理最后剩下attr()和css()方法没有处理,因为这两个方法当时并不自计划中,是写着写着突然想到的,一时间没有特别好的思路,当时已十一点多了,就去 ...

  4. 一个用微软官方的OpenXml读写Excel 目前网上不太普及的方法。

    新版本的xlsx是使用新的存储格式,貌似是处理过的XML. 传统的excel处理方法,我真的感觉像屎.用Oldeb不方便,用com组件要实际调用excel打开关闭,很容易出现死. 对于OpenXML我 ...

  5. 机械革命 USB装系统各种坑

    买了个号称超强性价比的游戏本- 机械革命, i7+ssd+hd+4G RAM+ GTX850M, 很直接, 直接出厂就一个DOS系统,回来要自己装机. 好吧, 先下了个大白菜软件,用来刻录ISO系统到 ...

  6. 解决服务器每次都要输入Enter PEM pass phrase

    今天架设好Python的HTTPS云服务器, 发现每次连接都要Enter PEM pass phrase 把服务器端的key里面的key剥离掉就好了 openssl rsa -in server.ke ...

  7. Java-异常处理练习

    1.建立exception包,编写TestException.java程序,主方法中有以下代码,确定其中可能出现的异常,进行捕获处理. package Yichang; public class Te ...

  8. Atitit usrqbg1821 Tls 线程本地存储(ThreadLocal Storage 规范标准化草案解决方案ThreadStatic

    Atitit usrqbg1821 Tls 线程本地存储(ThreadLocal Storage 规范标准化草案解决方案ThreadStatic 1.1. ThreadLocal 设计模式1 1.2. ...

  9. OutputCache概念学习

    目录 OutputCache概念学习 OutputCache属性详解(一) OutputCache属性详解(二) OutputCache属性详解(三) OutputCache属性详解(四)— SqlD ...

  10. 简单的JPA注解例子

    package ssh.entity; import java.math.BigDecimal; import java.util.Date; import javax.persistence.*; ...