EF CodeFirs 代码迁移、数据迁移
最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来。
十年河东十年河西,莫欺少年穷
学无止境,精益求精
标题叫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 代码迁移、数据迁移的更多相关文章
- 20.1翻译系列:EF 6中自动数据迁移技术【EF 6 Code-First系列】
原文链接:https://www.entityframeworktutorial.net/code-first/automated-migration-in-code-first.aspx EF 6 ...
- 一步一步学EF系列三【数据迁移】
我们每篇的内容都不多,所以希望在学习的过程中最后能亲自敲一下代码 这样更有利于掌握. 我们现在接着上篇的例子,我们现在给随便的表增加一个字段 CreateTime 创建日期 运行一下 看看会怎么样 修 ...
- EF架构~CodeFirst数据迁移与防数据库删除
回到目录 本文介绍两个概念,防数据库自动删除,这是由于在code first模式下,当数据实体发生变化时,会对原来数据库进行删除,并将新数据表添加进来,但这对于我们的运营环境数据库,是万万不能接受的, ...
- EF 中 Code First 的数据迁移以及创建视图
写在前面: EF 中 Code First 的数据迁移网上有很多资料,我这份并没什么特别.Code First 创建视图网上也有很多资料,但好像很麻烦,而且亲测好像是无效的方法(可能是我太笨,没搞成功 ...
- EF Code First 数据迁移配置
这里我想讲清楚code first 数据迁移的两种模式,还有开发环境和生产环境数据迁移的最佳实践. 1.1 数据迁移综述 EF Code first 虽然已经有了几种不同的数据库初始化策略,但是大部分 ...
- 20.2.翻译系列:EF 6中基于代码的数据库迁移技术【EF 6 Code-First系列】
原文链接:https://www.entityframeworktutorial.net/code-first/code-based-migration-in-code-first.aspx EF 6 ...
- EF Code-First数据迁移
Code-First数据迁移 首先要通过NuGet将EF升级至最新版本. 新建MVC 4项目MvcMigrationDemo 添加数据模型 Person 和 Department,定义如下: usi ...
- 【EF】EF Code-First数据迁移
Code-First数据迁移 首先要通过NuGet将EF升级至最新版本. 新建MVC 4项目MvcMigrationDemo 添加数据模型 Person 和 Department,定义如下: usi ...
- 【odoo】[经验分享]数据迁移注意事项
[odoo14]经典好书学习没有烂尾,主体已完成,可移步了解.https://www.cnblogs.com/xushuotec/p/14428210.html 背景 近期,有朋友打算上odoo系统. ...
随机推荐
- Winform 可取消的单选按钮(RadioButton)
在项目当中有一组选择项,要求单选并且可以取消,但Winform提供的单选按钮一旦选择上就不能取消了,经过研究和测试,总算搞定了功能的要求,主要代码如下,就不一一说明了,代码里有注释,给有需要的朋友一个 ...
- 哪些JavaScript IDE最好用?
阅读本文之前,分享大家一张图片,看图会发现JavaScript开发需求最高,占比达到42.84%,因此掌握JavaScript语言好工作就不愁啦,工欲善其事必先利其器,那么选择IDE来开发是至关重要的 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(七):解决用户上下文(Session)问题
从这篇文章中我们已经了解了微信公众平台消息传递的方式,这种方式有一个先天的缺陷:不同用户的请求都来自同一个微信服务器,这使得常规的Session无法使用(始终面对同一个请求对象,况且还有对方服务器Co ...
- 使用Async和Await进行异步编程(C#版 适用于VS2015)
你可以使用异步编程来避免你的应用程序的性能瓶颈并且加强总体的响应.然而,用传统的技术来写异步应用是复杂的,同时编写,调试和维护都很困难. VS2012介绍了简单的方法,那就是异步编程,它在.Net F ...
- 大叔也说Xamarin~Android篇~ListView里的Click事件并获取本行的其它元素
回到目录 我原创,我贡献,我是仓储大叔 本篇大叔原创,本着对技术的热爱去研究它,把成果分享给国人!大叔始终相信一句话:你只有选择一个感兴趣的工作,你才能更好的发挥你的潜力,而这一切都建立在你不断研究, ...
- [转]五种开源协议的比较(BSD,Apache,GPL,LGPL,MIT)
当Adobe.Microsoft.Sun等一系列巨头开始表现出对"开源"的青睐时,"开源"的时代即将到来!现今存在的开源协议很多,而经过Open Source ...
- localStorage使用
localStorage使用 需要注意的是,HTML5本地存储只能存字符串,任何格式存储的时候都会被自动转为字符串,所以读取的时候,需要自己进行类型的转换. 支持的情况如上图,IE在8.0的时候就支持 ...
- jquerymobile仿微信 - 01
jquerymobile仿微信 - 01 jquerymobile的组件感觉不咋地哇 本地调试最好是开一个web server,不然数据访问会有问题 <div data-role="p ...
- [java]输入一个算术表达式输出结果
动手有益. 输入一个表达式,没有括号,数字小于0-9之间,输出计算结果,所有的中间结果化为整形.例如: 输入:3+8×2/9-2 输出:2 /** * input a calculate stri ...
- JAVA_Collection容器
因为项目的需要,今天抽时间把JAVA中的容器复习了一下,为了以后的不时之需,现在把它记下来. 容器有其名,知其意,用来盛放数据的集合,JAVA中为我们提供了三种容器类:set.list.map,三种容 ...