ThinkingHome.Migrator版本的.NET Core平台上的数据库架构迁移

你好 今天,我发布了新版本的ThinkingHome.Migrator-一种用于将数据库架构版本迁移到.NET Core平台的工具。


程序包发布在NuGet中 ,编写了详细的文档 。 您已经可以使用新的迁移器,我将告诉您它的外观,为什么具有版本号3.0.0(尽管这是第一个发行版)以及在有EF迁移FluentMigrator时为何需要它。


一切如何开始


9年前,在2009年,我担任ASP.NET开发人员。 当我们发布项目时,一个特别的人熬夜,同时更新服务器上的文件,并用手更新产品中的数据库来执行SQL脚本。 我们搜索了一种可以自动执行此操作的工具,并找到了Migrator.NET项目。


迁移者当时提出了一个新想法-以迁移的形式设置数据库更改。 每次迁移都包含一小部分更改,并具有一个版本号,数据库在完成后将进入该版本号。 迁移者本人会跟踪版本并以必要的顺序执行必要的迁移。 尤其酷的是,该迁移器允许为每个迁移设置反向更改。 迁移器开始时可以将版本设置为低于当前版本,并且它将自动将数据库回滚到该版本,并以相反的顺序执行必要的迁移。


[Migration(1)] public class AddAddressTable : Migration { override public void Up() { Database.AddTable("Address", new Column("id", DbType.Int32, ColumnProperty.PrimaryKey), new Column("street", DbType.String, 50), new Column("city", DbType.String, 50) ); } override public void Down() { Database.RemoveTable("Address"); } } 

那位移民有很多错误。 他不知道如何使用默认模式以外的数据库模式。 在某些情况下,它会生成不正确的SQL查询,并且如果您指定了不存在的版本号,它将陷入无限循环。 结果,我和我的同事分叉了项目并在那里修复了错误。


那时不存在带有fork和pull请求的GitHub.com( 迁移代码在code.google.com上 )。 因此,我们尤其不必费心确保更改恢复到原始项目中-我们只是看到了副本并自己使用了它。 随着时间的流逝,我们重写了大部分项目,而我成为了项目的主要维护者。 然后,我在Google代码上发布了我们迁移器的代码,在Habr上了一篇文章 。 于是就有了ECM7.Migrator。


在对迁移器进行工作期间,我们几乎完全重写了它。 同时,他们稍微简化了API,并通过自动测试涵盖了所有内容。 就个人而言,我真的很喜欢使用发生的事情。 与最初的移民不同,这里有一种可靠的感觉,也没有感觉到发生了奇怪的魔术。


事实证明,不仅我喜欢我们的移民者。 据我所知,它被用于相当大的公司。 我知道ABBYY,BARS Group和Concert.ru。 如果您输入搜索查询“ ecm7 migrator”,您将在结果文章中看到有关它的信息,简历中提及了学生工作中的使用。 有时我收到陌生人的来信,提出疑问或感激之词。


2012年之后,该项目几乎没有开发。 它当前的功能涵盖了我所拥有的所有任务,并且我认为不需要完成某些事情。


ThoughtHome.Migrator


去年,我开始在.NET Core上进行项目 。 有必要使插件连接成为可能,并且插件应该能够为自己创建必要的数据库结构以将其数据存储在那里。 这正是迁移者非常适合的任务。


EF迁移


我使用实体框架核心来处理数据库,因此我尝试的第一件事是EF迁移。 不幸的是,几乎立即我不得不放弃使用它们的想法。


Entity Framework迁移将一堆依赖项拖到项目中,并且在启动时会做一些特殊的魔术。 向左走/向右走-您遇到限制。 例如,出于某种原因,Entity Framework的迁移必须与DbContext同一程序DbContext 。 这意味着将无法在插件内部存储迁移。


流浪汉


当很明显EF迁移不适合我时,我在google中寻找解决方案,并发现了几个开源迁移器。 从NuGet上的下载数量和GitHub上的stars来看,其中最先进的是FluentMigrator 。 FM非常好! 他了解很多,并且具有非常方便的API。 最初,我认为这是我所需要的,但是后来发现了一些问题。


主要问题是FluentMigrator不知道如何考虑同一数据库中的多个版本序列。 如上所述,我需要在模块化应用程序中使用迁移器。 必须可以独立安装和更新模块(插件)。 FluentMigrator具有端到端版本编号。 因此,在不影响其余插件的数据库结构的情况下,不可能从数据库执行/回滚一个插件的迁移。


我尝试使用标签来组织所需的行为,但这也不是您所需要的。 FluentMigrator不存储已完成迁移的标签信息。 另外,标签与迁移而不是程序集相关。 鉴于迁移的入口点正好是程序集,因此这很奇怪。 原则上,可能可以通过这种方式进行并行版本控制,但是您需要在迁移器上编写一个相当复杂的包装器。


将ECM7.Migrator移植到.NET Core


最初,甚至没有考虑过此选项。 当时,.NET Core的当前版本为1.1,其API与ECM7.Migrator在其中工作的.NET Framework兼容性很差。 我确信将其移植到.NET Core既困难又耗时。 当没有选择“完成”时,我决定尝试一下。 这项任务比我预期的要容易。 令人惊讶的是,它几乎立即起作用。 只需要较小的编辑。 由于测试涵盖了迁移器的逻辑,因此所有出现故障的地方都可以立即看到,因此我迅速对其进行了修复。


现在,我只为四个DBMS移植了适配器:MS SQL Server,PostgreSQL,MySQL,SQLite。 我没有用于Oracle的端口适配器(因为.NET Core仍然没有稳定的客户端),MS SQL Server CE(因为它仅在Windows下工作并且我没有运行它的地方)和Firebird(因为它不太受欢迎,请稍后移植)。 原则上,如果您需要为这些DBMS或其他DBMS设置提供程序-这非常简单。


新迁移的源代码在GitHub上 。 在Travis CI中为每个DBMS配置了测试启动 。 编写了可以从NuGet轻松安装的命令行实用程序( .NET Core全局工具 )。 文档是书面的-我非常努力地进行了详细而清晰的编写,似乎确实发生了。 您可以使用!


关于名字的一点...


新的迁移器与旧的迁移器不具有向后兼容性。 它们在不同的平台上工作,并且具有不同的API。 因此,该项目以不同的名称发布。


这个名称是由ThinkingHome项目选择的,为此我移植了一个迁移器。 实际上,ECM7.Migrator也以我当时正在从事的项目命名。


也许最好选择一些中性的名称,但是我没有想到有什么好的选择。 如果您知道这一点,请在评论中写。 重命名所有内容还为时不晚。


版本号指示为3.0.0,因为 新的迁移器是旧迁移器的逻辑延续。


快速上手


因此,让我们尝试使用迁移器。


所有数据库更改都记录在迁移代码中-用编程语言(例如,用C#)编写的类。 迁移类继承自ThinkingHome.Migrator.Framework包中的Migration基类。 在它们中,您需要重写基类的方法: Apply (应用更改)和Revert (回滚更改)。 在这些方法中,开发人员使用特殊的API描述需要在数据库上执行的操作。


同样,迁移类必须标记为[Migration]属性,并指示在进行这些更改后数据库将进入的版本。


迁移范例


 using ThinkingHome.Migrator.Framework; [Migration(12)] public class MyTestMigration : Migration { public override void Apply() { //  :   Database.AddTable("CustomerAddress", new Column("customerId", DbType.Int32, ColumnProperty.PrimaryKey), new Column("addressId", DbType.Int32, ColumnProperty.PrimaryKey)); } public override void Revert() { //  :   Database.RemoveTable("CustomerAddress"); //     ,  //  Revert    } } 

怎么跑


迁移被编译为.dll文件。 之后,您可以使用migrate-database控制台实用程序执行数据库更改。 首先, 从NuGet安装它。


 dotnet tool install -g thinkinghome.migrator.cli 

运行migration migrate-database ,指定所需的DBMS类型,连接字符串以及进行迁移的.dll文件的路径。


 migrate-database postgres "host=localhost;port=5432;database=migrations;" /path/to/migrations.dll 

通过API运行


您可以从自己的应用程序通过API执行迁移。 例如,您可以编写一个应用程序,该应用程序在启动时会自行创建所需的数据库结构。


为此,将NuGet的ThinkingHome.Migrator程序包和具有所需DBMS转换提供程序的程序包连接到您的项目。 之后,创建ThinkingHome.Migrator.Migrator类的实例,并调用其Migrate方法,并将所需的数据库版本作为参数传递。


 var version = -1; //  -1     var provider = "postgres"; var connectionString = "host=localhost;port=5432;database=migrations;"; var assembly = Assembly.LoadFrom("/path/to/migrations.dll"); using (var migrator = new Migrator(provider, connectionString, assembly)) { migrator.Migrate(version); } 

顺便说一下,您可以将其 FluentMigrator 启动示例进行比较。


结论


我试图制作一个简单的工具,没有上瘾和复杂的魔法。 看来效果很好。 该项目很长一段时间都不是很简陋,测试中涵盖了所有内容,还有俄语的详细文档 。 如果您使用的是.NET Core 2.1,请尝试使用新的迁移器 。 您很有可能也会喜欢它。

Source: https://habr.com/ru/post/zh-CN414867/


All Articles