EF Codefirst学习系列四:数据库迁移

1、写在前头

  清明小假转眼过去,又迎来上班和下班循环的日子,工作之余仍然学习一些知识提升下自己。前面说了种子数据,但用过的人很快就会发现,这种模式其实也无法满足咱们蠢蠢欲动的需求。种子数据是固定的,添加起来也比较麻烦,况且也无法覆盖所有的条件达到测试的目的,所以这个功能其实有点鸡肋,故而咱们今天再来唠唠数据库迁移功能,这个是咱们的终极目的——无损数据更改数据库。

2、开搞

  2.1开户数据库迁移功能和初始化

  所谓迁移一般不会伤其根本,就像移树,如果把它搞死了,那这个动作就算失败咯,故而数据库迁移应该建立在对数据不进行丢失和改变的基础上进行的。数据库迁移集合在EntityFramework中,故而要想使用该功能要先安装EntityFramework,使用它还需要用到另一个东西:程序包管理器控制台(工具->NuGet程序包管理器->程序包管理器控制台),其实这货就是个命令行,通过NuGet集成在了VS里面,所以写起来跟在dos窗口里面差不多,只不过颜值高一点,操作方便点啥的,此文以下篇幅中简称控制台。

  好了,步入正题,想使用数据库迁移要先在项目里面打开数据库迁移功能,在控制台中输入如下脚本:

Enable-Migrations

该脚本指定当前项目开启数据库迁移功能,注意把默认项目设置为你的目标项目,具体位置在控制台上边程序包源的右边。

  成功执行后可以发现项目会发生变化,多了一个文件夹Migrations,里面有一个类Configuration.cs文件,代码如下:

internal sealed class Configuration : DbMigrationsConfiguration<EF.Infrastruct.EFDbContext>
    {
        public Configuration()
        {
            AutomaticMigrationsEnabled = false;
        }

        protected override void Seed(EF.Infrastruct.EFDbContext context)
        {
            //  This method will be called after migrating to the latest version.

            //  You can use the DbSet<T>.AddOrUpdate() helper extension method 
            //  to avoid creating duplicate seed data. E.g.
            //
            //    context.People.AddOrUpdate(
            //      p => p.FullName,
            //      new Person { FullName = "Andrew Peters" },
            //      new Person { FullName = "Brice Lambson" },
            //      new Person { FullName = "Rowan Miller" }
            //    );
            //
        }
    }

其中AutomaticMigrationsEnabled是设置是否开户自动数据库迁移,这里是false,因咱们只是开启了数据库迁移,并没有设置自动迁移,若想让其值为true,只需稍稍改动下刚刚的指令即可,如下:

Enable-Migrations -EnableAutomaticMigration

这样自动生成的文件中AutomaticMigrationsEnabled值就会为true。那这个值是干啥的呢?别急,稍后讲。

其实正常情况下Migrations文件夹下还会有一个类,以InitialCreate结尾。但是我本次测试连接字符串写错了导致没有生成该文件,这个文件主要作用是初始化当前项目的数据库基本情况,比如有没有建立数据库啊,当前的模型状态如何啊什么的,给以后的操作参考。现在由于连接字符串出错初始化失败该怎么办呢?不用担心,EF这个暖男旱就预料到了这些,为我们提供了贴心的初始化指令:

Add-Migration InitialCreate

执行完毕后又生成了一个文件201604060530313_InitialCreate.cs,看看代码是什么:

public partial class InitialCreate : DbMigration
    {
        public override void Up()
        {
            CreateTable(
                "dbo.Courses",
                c => new
                    {
                        CId = c.Int(nullable: false, identity: true),
                        Name = c.String(),
                        StudentId = c.Int(nullable: false),
                    })
                .PrimaryKey(t => t.CId)
                .ForeignKey("dbo.Students", t => t.StudentId, cascadeDelete: true)
                .Index(t => t.StudentId);
            
            CreateTable(
                "dbo.Students",
                c => new
                    {
                        SId = c.Int(nullable: false, identity: true),
                        Name = c.String(),
                    })
                .PrimaryKey(t => t.SId);
            
        }
        
        public override void Down()
        {
            DropForeignKey("dbo.Courses", "StudentId", "dbo.Students");
            DropIndex("dbo.Courses", new[] { "StudentId" });
            DropTable("dbo.Students");
            DropTable("dbo.Courses");
        }
    }

Migrations文件夹下这种名字的类都是记录数据库迁移历史的文件,每一个类里都有两个方法,Up和Down,Up是往上升级时使用,Down是往下降级时使用,因为这次是初始化操作,由于我新建了一个项目,目前还没有建库所以Up里面是建表的操作,Down里面是销毁表和外键的操作。每次执行数据库迁移都会生成这样一个文件,用于还原或生成脚本时使用,下面还会看到。

  2.2数据库迁移

  其实我们可以总结一下日常开发可能需要数据库迁移的情况,跟数据的CRUD真的差不多,增加/删除/修改表增加/删除/修改字段,对应到代码里面就是模型和属性,所以我们可以根据这些情况来对应的看怎么进行数据库迁移。

  先看看表/模型的CUD:增加。接着上一篇的例子有了学生和课程后咱们再新建一个模型,Teacher,光有学生没有老师玩什么。

public class Teacher
    {
        public int TId { get; set; }

        public string Name { get; set; }
    }

新建了一个模型如何进行迁移并更新到数据库呢,先在控制台执行以下指令: 

Add-Migration AddTeacher

响亮回车坐等EF为咱们搞定一切~~然而,啪啪啪三下清脆的响声,脸肿了,EF回馈如下信息:

Unable to generate an explicit migration because the following explicit migrations are pending: [201604060530313_InitialCreate]. Apply the pending explicit migrations before attempting to generate a new explicit migration.

啥意思呢,是说咱们上一步的初始化操作还在挂起中,得先把上一步的先搞定再说其他的。well,你牛X,听你的,那怎么搞定上一步呢,其实就是要把模型更改提交到数据库,请记住,每次进行更改后只是保存在了程序中,需要提交到数据库才行,不然更改只是被挂起,下面的改动将无法进行。执行以下命令即可将更改提交到数据库:

Update-Database -Verbose

这次没被打脸,成功执行,控制台唰唰的输出了一大堆的东西,仔细看可发现是建库和建表语句,每次提交都会生成更改sql语句。打开数据库管理器,发现多了一个数据库,里面有三张表:

两个是咱们的模型(有木有发现表名自动加了s变成了复数?EF贴心不?),一个是迁移记录。

这次再次执行上面新加Teacher指令,发现可成功执行,再执行提交数据库指令,可以发现又多了一个Teachers表(注意把Teacher加到DbContext中)。

老师是添加了,但是明显不够用啊,老师连个性别都木有?于是咱们加个Sex和Age属性,依次执行如下指令:

Add-Migration ModifyTeacher
Update-Database -Verbose

再看数据库,可发现Teachers表中已有了新加的两个属性。

说完新建和修改,该说删除了,聪明的朋友看完上面的两个指令应该知道具体的指令了:

Add-Migration DeleteTeacher

OK了,模型基本就这样了,那属性呢?你想想,属性变动不就是表的变动吗,我想你已经知道怎么做了吧。

  2.2恢复指定版本和生成脚本

  有时候你可能改错了或者需求改动了需要把数据库还原到以前的版本,EF能搞吗,完全木有问题:

Update-Database -TargetMigration:"201604060825214_ModifyTest"

这样就能将数据库还原到历史版本了,请注意高版本的Ef的话请不要带后缀名,否则会提示无法找到指定的文件。

除了这些牛叉的功能,EF还能生成Sql脚本:

Update-Database -Script -SourceMigration:"201604060758527_AddTest7" -TargetMigration:"201604060825214_ModifyTest"

生成的脚本能将数据库从Source还原到Target版本。值得一说的是source和target这两个参数均可省略,缺省值分别为最后一次迁移的版本和项目当前的版本。

  2.3自动迁移

  前面的EnableAutomaticMigration还记得吗,这是设置自动迁移的,为true时表示开启自动迁移功能,因为每次更改模型都要执行迁移指令太麻烦,能不能这样呢,在撸代码的过程中咱们可以随便更改模型,等到启动项目时一次性的将所有变更提交到数据库?答案是YES!操作如下:

1、将EnableAutomaticMigration设置为true;

2、在Global.asax中将数据库初始化方式设置为如下:

Database.SetInitializer(new MigrateDatabaseToLatestVersion<EFDbContext,Configuration>());

其中Configuration就是自动生成的Migrations文件夹下的Configuration.cs类。

3、在Configuration.cs中设置自动迁移时允许数据丢失:

AutomaticMigrationsEnabled = true;
AutomaticMigrationDataLossAllowed = true;

什么意思呢,假如我们在模型里删除了一个属性,那么数据库表就要删除一个字段,如果表中有值的话这个字段的值是不是就丢失了?如果数据丢失的责任你负责,那就OK,否则你去找微软的事他们真说不清,所以这算是微软的推锅免责声明:是你自己允许的,不怪我。

完成以上三个步骤,就能实现自动提交更改了,无需每次都要执行Migration指令了(我懒我骄傲~~~~)。

3、注意事项

  1、开户数据迁移后添加模型执行迁移时记得把模型添加到DbContext中,否则EF无法感知模型已经更改。

  2、在控制台执行过迁移命令后记得使用Update-Database指令提交到数据库,否则下次执行迁移命令时会提示有挂起的更改,无法执行。

能想到的就这么多啦,还有的欢迎补充~~

4、完工

  好了,其实数据迁移还是挺简单的对吗,关键是要自己手动测试下,相信很快就能学会的,有不会的和有疑问的可以评论里说下咱们一起研究啊,我会第一时间回复的。我也是在学习的过程中,有错误的地方欢迎吐槽和补充。

  另外大家如果看完整个系列的话会发现我一直在说怎么用但是很少说原理,因为我一向习惯先知其然再知其所以然,可能跟我入门时的方式有关吧,知道怎么用再去研究原理的放会更容易,而研究各人有不同的方法,我就不班门弄斧了。

posted @ 2016-04-07 11:00  愉快编程  阅读(598)  评论(0)    收藏  举报