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来做数据库迁移。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对www.887551.com的支持。