如何利用FluentMigrator实现数据库迁移
fluentmigrator
fluent migrator是一个基于.net的迁移框架,你可以像使用ruby on rails migrations一样使用它。fluent migrator的最新版本是3.13版,官网地址。 你可以使用c#编写数据库迁移类,而不需要编写任何sql脚本。从使用方式上看,它非常像ef/ef core的数据库迁移脚本,但是它支持的数据库类型比ef/ef core多的多,且不受限与ef/ef core。
支持的数据库列表
- microsoft sql server 2017
- microsoft sql server 2016
- microsoft sql server 2014
- microsoft sql server 2008
- microsoft sql server 2005
- microsoft sql server 2000
- microsoft sql server compact edition
- postgresql
- mysql 4
- mysql 5
- oracle
- oracle (managed ado.net)
- oracle (dotconnect ado.net)
- microsoft jet engine (access)
- sqlite
- firebird
- amazon redshift
- sap hana
- sap sql anywhere
- db2
- db2 iseries
fluent migrator提供了5个不同的类库来支持不同的场景。
package | 描述 |
---|---|
fluentmigrator | 创建数据库所需的基础程序集 |
fluentmigrator.runner | 进程内执行数据库迁移所需的程序集 |
fluentmigrator.console | 进程外执行数据库迁移所需的程序集,它兼容.net 4.0/4.5/.net core 2.0 |
fluentmigrator.msbuild | 兼容.net 4.0/4.5/.net standard 2.0的msbuild任务 |
fluentmigrator.dotnet.cli | 可执行数据库迁移的.net core cli工具 |
入门例子
这里我们首先演示一个最简单的数据库迁移例子,为一个mysql数据库添加一个日志表。
创建控制台程序
我们使用.net core cli创建一个.net core的命令行程序。
# 迁移脚本基础库 dotnet add package fluentmigrator # 迁移脚本运行库 dotnet add package fluentmigrator.runner # 针对mysql的迁移脚本支持库 dotnet add package fluentmigrator.runner.mysql # ado.net针对mysql的驱动器 dotnet add package mysql.data
添加第一个数据库迁移类
未了创建一个名为log的表,这里需要创建一个数据库迁移类
- log表中有2个字段,一个是id字段,一个是text字段
- id字段是int64类型的主键,且自增
- text字段是字符串字段
using fluentmigrator; namespace test { [migration(20180430121800)] public class addlogtable : migration { public override void up() { create.table("log") .withcolumn("id").asint64().primarykey().identity() .withcolumn("text").asstring(); } public override void down() { delete.table("log"); } } }
运行迁移类
编写完迁移类之后,我们就可以开始运行迁移类了。
fluent migrator有两种运行迁移脚本的方式。
- 使用进程内执行器(推荐)
- 使用进程外执行器
使用进程内执行器
所谓的进行内执行器,其实就是借助fluentmigrator.runner库,在程序内部手动调用imigrationrunner接口对象的migrateup方法执行数据库迁移。
这里我们可以修改program.cs文件如下。
class program { static void main(string[] args) { var serviceprovider = createservices(); using (var scope = serviceprovider.createscope()) { updatedatabase(scope.serviceprovider); } } private static iserviceprovider createservices() { return new servicecollection() //添加fluentmigrator基础服务 .addfluentmigratorcore() .configurerunner(rb => rb //添加mysql 5.0支持 .addmysql5() //配置连接字符串 .withglobalconnectionstring("server=localhost;port=3307;database=abc;uid=root;pwd=123456") //检索迁移配置 .scanin(typeof(addlogtable).assembly).for.migrations()) //启用控制台日志 .addlogging(lb => lb.addfluentmigratorconsole()) //构建服务提供器 .buildserviceprovider(false); } private static void updatedatabase(iserviceprovider serviceprovider) { //初始化进程内迁移构建器 var runner = serviceprovider.getrequiredservice<imigrationrunner>(); //执行迁移脚本 runner.migrateup(); } }
启动程序之后,迁移自动完成。
使用进程外执行器
如果你想使用进行外迁移执行器,这里首先需要保证你已经安装了.net core 2.1或以上版本的sdk, 因为你需要使用.net core 2.1之后新增的global tool功能。
这里我们可以使用命令行,添加fluentmigrator.dotnet.cli这个工具
dotnet tool install -g fluentmigrator.dotnet.cli
安装完成之后,我们就可以使用这个工具来做数据库迁移了
dotnet fm migrate -p mysql -c "server=localhost;port=3307;database=abc;uid=root;pwd=123456" -a ".\bin\debug\netcoreapp2.1\test.dll"
这个方法有3个参数, 第一个参数-p指定了数据库的类型,第二个参数-c指定了连接字符串,第三个参数-a指定了包含迁移类的程序集路径。
注意:其实这里还有第四个参数command, 可选值为down/up, 如果不指定,默认是up, 即运行所有还未运行过的数据库迁移类。
方法执行后,效果和进程内执行器的效果一致。
基本概念
在展示了一个简单示例之后,我们接下来看一下fluent migrator中的一些基本概念。
迁移(migrations)
fluent migrator中最基础的元素是迁移类,每个迁移类都需要继承自一个名为migration的抽象类,并实现两个抽象方法up和down, 顾名思义up方法即执行当前的数据库变更,down方法即回滚当前的数据库变更。
[migration(1)] public class createusertable : migration { public override void up() { create.table("users"); } public override void down() { delete.table("users"); } }
这里你可能注意到迁移类的头部,有一个migration的特性,它的值是1, 这里其实是指定了迁移类执行的顺序,编号越小的迁移类越先执行(有一部分开发人员系统会使用当前日期的yyyymmddhhmmss格式来标记迁移类),这个编号必须是唯一的,不能重复。
fluent接口(fluent interface)
fluent migrator提供非常丰富的fluent api, 我们可以使用这些api来创建表,列,索引。 基本上你能用到的大部分场景它都支持。
创建表达式(create expression)
你可以使用它创建表达式来添加表,列,索引,外键,组织结构(schema)
create.table("users") .withidcolumn() .withcolumn("name").asstring().notnullable();
注:withidcolumn()是一个扩展方法,它等价于.withcolumn("id").asint32().notnullable().primarykey().identity();
create.foreignkey() .fromtable("users").foreigncolumn("companyid") .totable("company").primarycolumn("id");
变更表达式(alter expression)
用来变更已存在的表和列
alter.table("bar") .addcolumn("somedate") .asdatetime() .nullable();
alter.table("bar") .altercolumn("somedate") .asdatetime() .notnullable();
alter.column("somedate") .ontable("bar") .asdatetime() .notnullable();
删除表达式(delete expression)
用来删除表,列,外键,组织结构(schema)
delete.table("users");
删除多个列(delete multiple columns)
fluent migrator也提供了一个删除多列的语法
delete.column("allowsubscription").column("subscriptiondate").fromtable("users");
执行脚本(execute expression)
允许你执行自定义的sql脚本或执行指定的sql脚本文件
execute.script("myscript.sql"); execute.embeddedscript("updatelegacysp.sql"); execute.sql("delete table users");
这里embeddedscript方法也是执行指定的sql脚本文件,但是它的文件来源embbed resource中读取。如果你想使用embbedscript只需要将指定的sql脚本文件的build action属性设置为embbed resource即可。
重命名表达式(rename expression)
允许重命名表或列
rename.table("users").to("usersnew"); rename.column("lastname").ontable("users").to("surname");
数据操作表达式(data expressions)
允许对数据库数据进行新增/修改/删除操作
insert.intotable("users").row(new { firstname = "john", lastname = "smith" });
delete.fromtable("users").allrows(); //删除所有行 delete.fromtable("users").row(new { firstname = "john" }); //删除所有firstname = john的数据行 delete.fromtable("users").isnull("username"); //删除所有username为空的数据行
update.table("users").set(new { name = "john" }).where(new { name = "johnanna" });
数据库类型判断表达式(ifdatabase expression)
允许根据数据库类型,执行不同的数据库迁移操作
ifdatabase("sqlserver", "postgres") .create.table("users") .withidcolumn() .withcolumn("name").asstring().notnullable(); ifdatabase("sqlite") .create.table("users") .withcolumn("id").asint16().primarykey() .withcolumn("name").asstring().notnullable();
组织结构存在表达式(schema.exists expressions)
用来判断组织结构是否已经存在,列如判断表是否存在,列是否存在等等。
if (!schema.table("users").column("firstname").exists()) { this.create.column("firstname").ontable("users").asansistring(128).nullable(); }
配置(profile)
fluent migrator还提供了一个profile的特性,使用该配置,开发人员可以对针对的不同的环境(开发环境,测试环境,生产环境等)运行不同的脚本。
[profile("development")] public class createdevseeddata : migration { public override void up() { insert.intotable( "user" ).row( new { username = "devuser1", displayname = "dev user" }); } public override void down() { //empty, not using } }
和ef/ef core的脚本迁移比较
fluent migrator的数据库脚本迁移与ef/ef core非常类似。
相似点:
- 当我们使用ef/ef core做数据库迁移的时候,会在当前数据库中创建一个__efmigrationshistory表,并在其中保存运行过的脚本id。
- 当我们使用fluent migrator做数据库迁移的时候,也会在数据库中创建一个versioninfo表,并在其中保存运行过的脚本id
区别:
- ef/ef core的迁移脚本是根据ef上下文配置以及最新的modelsnapshot自动生成的,更方便一些。fluent migrator的迁移脚本,都需要自己手动编写, 更灵活一些。
- ef/ef core每次自动生成的迁移文件一个cs文件一个design.cs文件,每个cs文件中包含了自动生成的脚本类,design.cs里面包含了针对当前迁移类的最新modelsnapshot, 所以重度使用ef/ef core, 最后累计生成的design.cs文件都会非常大。fluent migrator的每个迁移类都是自己编写的,只包含本次迁移的内容,所以体积更小。
总结
本篇中我描述了fluent migrator的一些基本用法,以及它与ef/ef core脚本迁移的区别, 如果你不是重度ef/ef core的使用者,可以尝试一下使用fluent migrator来做数据库迁移。
好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。
推荐阅读
-
如何利用FluentMigrator实现数据库迁移
-
如何利用percona-toolkit工具检查MySQL数据库主从一致性以及修复
-
axios如何利用promise无痛刷新token的实现方法
-
PHP查询并删除数据库多列重复数据的方法(利用数组函数实现)
-
如何利用SEO快速实现精准引流?
-
如何利用vue+vue-router+elementUI实现简易通讯录
-
Spring Boot利用@Async如何实现异步调用:自定义线程池
-
SEO如何利用新媒体推广网站软文 实现最大化传播
-
如何利用percona-toolkit工具检查MySQL数据库主从一致性以及修复
-
如何利用命令行轻松实现在关机前自动备份