EF CodeFirs 代码迁移、数据迁移

简介: 最近悟出来一个道理,在这儿分享给大家:学历代表你的过去,能力代表你的现在,学习代表你的将来。   十年河东十年河西,莫欺少年穷   学无止境,精益求精   标题叫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(10)]
        public string Name { get; set; }//姓名
        [StringLength(2)]
        public string Sex { get; set; }//性别
        [Unique(ErrorMessage="学号不允许重复")]
        [StringLength(18)]
        public string StudentNum { get; set; }//学号

        [StringLength(100)]
        public string StudentAddress { get; set; }//地址
    }

   Course模型类

    public class Course
    {
        [Key]
        public int Id { get; set; }
        [Required]
        [StringLength(20)]
        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(10)]
        public string schoolName { get; set; }//学校名称

        [StringLength(100)]
        public string StudentAddress { get; set; }//学校地址

        [StringLength(20)]
        public string StudentTel { get; set; }//学校热线
    }

   修改学生模型类如下:

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

        [StringLength(100)]
        public string StudentAddress { get; set; }//地址

        [StringLength(11)]
        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: 20),
                    })
                .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: 10),
                        Sex = c.String(maxLength: 2),
                        StudentNum = c.String(maxLength: 18),
                        StudentAddress = c.String(maxLength: 100),
                        StudentTel = c.String(maxLength: 11),
                    })
                .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: 10),
                        StudentAddress = c.String(maxLength: 100),
                        StudentTel = c.String(maxLength: 20),
                    })
                .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: 11));
        }

        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: 10),
                        StudentAddress = c.String(maxLength: 100),
                        StudentTel = c.String(maxLength: 20),
                    })
                .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: 10),
                        StudentAddress = c.String(maxLength: 100),
                        StudentTel = c.String(maxLength: 20),
                    })
                .PrimaryKey(t => t.schoolId);
            
        }
    }

   修改字段长度或字段名称

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

  增加表字段:

    public partial class FFF : DbMigration
    {
        public override void Up()
        {
            AddColumn("dbo.Student", "StudentPhowwwwne", c => c.String(maxLength: 11));
        }
        
        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: 11));
        }
    }

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

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

   最新的[__MigrationHistory]数据

   

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

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

   @陈卧龙的博客

相关文章
|
5月前
|
关系型数据库 分布式数据库 API
PolarDB产品使用问题之迁移过程中出现报错,是什么原因
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
6月前
|
SQL Oracle 关系型数据库
xtts 迁移注意
xtts 迁移注意
47 9
|
存储 SQL 关系型数据库
MySQL5.7到8.0的升级迁移!高效的MySQL跨版本迁移技术解读
NineData是一种高效、稳定且易于使用的迁移工具,可帮助用户将MySQL 5.7迁移到MySQL 8.0。由于MySQL 5.7已经结束生命周期并不再获得技术支持,因此迁移至MySQL 8.0是必要的。NineData提供了数据复制功能,包括数据迁移和实时同步,还支持双向复制。与传统迁移方法相比,NineData具有简单易用、数据一致、强劲性能和高可靠性的优势。它提供了完善的观测和干预能力,保障迁移的成功。此外,NineData还提供了对比功能,确保数据的一致性。通过简单的配置过程,用户可以实现自动化的数据迁移。NineData还提供了完善的观测和干预能力,帮助用户追踪迁移进展并诊断和修复
1368 3
|
7月前
|
关系型数据库 数据库连接 分布式数据库
我想迁移下数据,用新镜像启动下PolarDB,这个有什么好的办法吗?
我想迁移下数据,用新镜像启动下PolarDB,这个有什么好的办法吗?
72 0
|
cobar 运维 Oracle
yugong阿里巴巴去Oracle数据迁移同步工具(全量+增量,目标支持MySQL/DRDS)
2008年,阿里巴巴开始尝试使用 MySQL 支撑其业务,开发了围绕 MySQL 相关的中间件和工具,Cobar/TDDL(目前为阿里云DRDS产品),解决了单机 Oracle 无法满足的扩展性问题,当时也掀起一股去IOE项目的浪潮,愚公这项目因此而诞生,其要解决的目标就是帮助用户完成从 Oracle 数据迁移到 MySQL 上,完成去 IOE 的重要一步工作。
yugong阿里巴巴去Oracle数据迁移同步工具(全量+增量,目标支持MySQL/DRDS)
|
数据库
TD8.0迁移到QC9.2,自动迁移失败,手动迁移
源机器A:TD8.0+SQLServer2000目标机器B:QC9.2+SQLServer2000 一:自动迁移,1、在A在A上安装QC迁移工具,然后在B中点击TOOLS>>Migration Tool,打算迁移,报错如下:    解决办法:查找TDProjectMigrationProj.
942 0
|
PHP 数据库 存储
phinx数据库脚本迁移工具
phinx数据库脚本迁移工具 Phinx 可以使用 Composer 进行安装,Composer是一个PHP依赖管理工具。更多信息请访问 Composer 官网。 Phinx 至少需要PHP 5.
1491 0
|
关系型数据库 MySQL 数据库