EF Core + Oracle:如何使迁移成为幂等



通常,EF Core框架与另一种Microsoft产品MS SQL结合使用。但是,这不是教条。例如,在CUSTIS,我们用C#编写业务逻辑,然后使用Oracle来管理数据库。EF Core具有出色的迁移机制,但就我们而言,它们不是幂等的。事实是,Oracle和许多其他数据库(例如MySQL)不支持事务性DDL这意味着,如果迁移位于中间位置,则无法回滚或回滚。在没有MS SQL的情况下如何实现向EF Core的幂等迁移?

背景


我们公司拥有一个相当强大的工具,可以在Oracle数据库上安装补丁,我们在许多项目中都使用了该工具。它是在没有Liquibase,EF迁移和其他开放工具的情况下编写的。Patcher使您可以处理数百个数据库,跟踪安装历史记录,查看日志,存储机密等等。用于更改数据库的脚本以SQL或m4宏的形式编写。在他们的帮助下,您可以修改其他结构:创建表,列和其他对象。此外,m4宏是幂等的。这意味着,例如,如果您尝试创建表,该脚本不会掉落,但是会看到它已经存在并跳过创建。

假设安装补丁的脚本包含两个操作:

  1. 创建表A。
  2. 创建表B。

如果脚本在第一次操作后崩溃,则表A仍保留在Oracle中,重新应用补丁程序将正常工作:脚本将验证A是否已存在,因此将立即进行第二次操作。

除了优点外,修补程序还具有一个缺点-该工具是封闭的,仅在CUSTIS中使用。开发人员必须学习与他合作,而在公司之外,这种经历并不是很有价值。此外,修补程序不支持“代码优先”操作模式,因此所有用于更改数据库结构的脚本都必须手动编写。

我们想尝试一些现成的机制来安装补丁并选择迁移。在2019年底,刚刚为客户启动了另一个项目,我们决定在该项目上测试一种新方法。这种机制的主要问题是迁移的非全能性。

问题


在MS SQL中,DDL语句或迁移链作为单个复合事务执行。发生中断时,操作将被完全取消。Oracle DDL是非事务性的,因此迁移下降将导致数据库状态不一致。

让我们回到由两个操作组成的补丁:创建表A和B。如果迁移器落在第一个之后,Oracle将保留在表A中。重新启动将不起作用-操作员CREATE TABLE将不喜欢表A已经存在。它还将无法回滚迁移:EF Core仅在过程的最后才向系统表写入迁移已完成。从EF Core的角度来看,如果迁移尚未完成,则没有任何回滚。

决断


在Internet上寻找适用于Oracle的现成解决方案的努力没有取得结果。我所发现的只是有关在使用EF时如何编写和  安装补丁的文章。稍后在StackOverflow上,我想到了制作IMigrationsSqlGenerator的想法。该接口负责生成处理EF操作的SQL代码。

Oracle.EntityFrameworkCore包含OracleMigrationsSqlGenerator,实现IMigrationsSqlGenerator。例如,如果您想添加一列,将生成以下代码:

ALTER TABLE MY_TABLE ADD (MY_COLUMN DATE)

然后,将代码传递给其他类以在数据库中运行。

首先,我尝试覆盖一对OracleMigrationsSqlGenerator操作。事实证明,这项任务很可行,我开始写一个等幂的移民。这就是CUSTIS.OracleIdempotentSqlGenerator产生的方式

在进行操作EF之前,我们的迁移器将检查是否已执行过。例如,这样添加一列:

DECLARE
    i NUMBER;
BEGIN
    SELECT COUNT(*) INTO i
    FROM user_tab_columns
    WHERE table_name = UPPER('MY_TABLE') AND column_name = UPPER('MY_COLUMN');
    IF I != 1 THEN
        EXECUTE IMMEDIATE 'ALTER TABLE MY_TABLE ADD (MY_COLUMN DATE)';  
    END IF;       
END;

使用


使用该软件包非常简单-您只需要IMigrationsSqlGenerator在正确的上下文中替换它即可

public class MyDbContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.ReplaceService<IMigrationsSqlGenerator, IdempotentSqlGenerator>();
    }
}

迁移是由EF Core标准工具形成和设置的

dotnet ef migrations add v1.0.1
dotnet ef database update

CUSTIS.OracleIdempotentSqlGenerator中 规定的一般方法可以在为MySQL,MariaDB,Teradata,AmazonAurora和其他不进行DDL交易的数据库编写的生成器中实现。

参考文献


该软件包可从GitHub 上的NuGet
Sources获得

All Articles