SuperAPI AI 工具集

migrating-oracle-to-postgres-data-access-code

编程与开发

查看项目

更新于 10-16 10:16

技能介绍

用途

本技能用于将单个 .Postgres 副本项目的 C# 数据访问层从 Oracle(Oracle.ManagedDataAccess)迁移到 PostgreSQL(Npgsql),按 Reports/{ProjectName}/MigrationChecklist.md 逐项执行迁移工作项。

前置条件

  • 已存在 .Postgres 项目副本(在 Phase 5 设置中创建)。
  • Reports/{ProjectName}/MigrationChecklist.md 存在,并作为迁移内容的依据来源。
  • Reports/{ProjectName}/OracleRiskAnalysis.md 存在,用于交叉参考行为差异。

工作流程

进度清单:

  • [ ] 步骤 1:替换 NuGet 包
  • [ ] 步骤 2:更新连接字符串配置
  • [ ] 步骤 3:重写 ADO.NET 类型引用
  • [ ] 步骤 4:修正 DbType 映射
  • [ ] 步骤 5:迁移存储过程调用
  • [ ] 步骤 6:处理 Oracle 专有 SQL 与语法
  • [ ] 步骤 7:构建并验证

步骤 1:替换 NuGet 包

在 .Postgres 项目的 .csproj 中:移除 Oracle.ManagedDataAccess.Core、Oracle.EntityFrameworkCore 及其他 Oracle.* 包;添加 Npgsql(用于 ADO.NET)和/或 Npgsql.EntityFrameworkCore.PostgreSQL(用于 EF Core)。保持版本锁定与目标 .NET 版本一致,不引入比解决方案中同类包更新的版本。如果项目范围使用了 System.Data 抽象(IDbConnection、IDbCommand),表层代码可能需要更少改动——先识别它们。

步骤 2:更新连接字符串配置

在 appsettings.json、appsettings.{env}.json、web.config、app.config 或环境变量配置中定位 Oracle 连接字符串,替换为 Npgsql 兼容格式:Host=localhost;Port=5432;Database=mydb;Username=myuser;Password=mypassword。不要硬编码凭据,沿用既有配置机制(如环境变量、密钥管理器、IConfiguration)。仅当连接字符串键名是 Oracle 专有时才更新(如 OracleConnection),尽量保留相同键名以减少应用配置改动。

步骤 3:重写 ADO.NET 类型引用

将 Oracle 专有 ADO.NET 类型替换为 Npgsql 等价类型:OracleConnection → NpgsqlConnection;OracleCommand → NpgsqlCommand;OracleDataReader → NpgsqlDataReader;OracleDataAdapter → NpgsqlDataAdapter;OracleParameter → NpgsqlParameter;OracleTransaction → NpgsqlTransaction;OracleException → NpgsqlException;OracleDbType → NpgsqlDbType(来自 NpgsqlTypes 命名空间)。相应更新 using 指令(using Oracle.ManagedDataAccess.Client → using Npgsql)。如果代码库通过 DI 注册 IDbConnection/IDbCommand 抽象,只需更新 DI 注册与连接字符串,消费代码可能无需改动。

步骤 4:修正 DbType 与 NpgsqlDbType 映射

Oracle 参数类型与 Npgsql 并非一一对应。逐一审查每个设置显式类型的 OracleParameter(现为 NpgsqlParameter):

  • OracleDbType.Varchar2:使用 NpgsqlDbType.Varchar 或省略(Npgsql 从值推断)
  • OracleDbType.Clob:使用 NpgsqlDbType.Text
  • OracleDbType.Number:根据精度使用 NpgsqlDbType.Numeric 或 NpgsqlDbType.Integer
  • OracleDbType.Date:使用 NpgsqlDbType.Date(仅日期)或 NpgsqlDbType.Timestamp(含时间)
  • OracleDbType.TimeStamp:使用 NpgsqlDbType.Timestamp
  • OracleDbType.RefCursor:使用 NpgsqlDbType.Refcursor,见步骤 5
  • OracleDbType.Char:使用 NpgsqlDbType.Char

对于 Oracle 从值推断类型的参数,Npgsql 也会推断——显式设置类型通常不必要,可移除。

步骤 5:迁移存储过程调用

Oracle 与 PostgreSQL 的存储过程调用差异明显:

  • 命令类型:函数调用保留 CommandType.StoredProcedure。对于使用 OUT 参数的过程,某些 Npgsql 版本要求 CommandType.Text 配合 CALL proc_name(...) 语法——需针对目标 Npgsql 版本验证。
  • RefCursor 处理:Oracle 以输出参数返回 ref 游标,PostgreSQL 返回方式不同。对于 RETURNS TABLE / RETURNS SETOF,直接使用 ExecuteReader(),无需游标参数;对于 RETURNS refcursor,在事务内调用,从输出参数读取游标名,然后执行 FETCH ALL IN "<cursor_name>"。移除任何 Oracle 专有的游标包装代码(如 OracleRefCursor)。
  • OUT 参数:PostgreSQL 存储过程使用 INOUT 或函数返回值。验证参数方向与迁移后的过程签名匹配。
  • 序列 NEXTVAL:将 SELECT {SEQUENCE}.NEXTVAL FROM DUAL 替换为 SELECT nextval('{sequence_name}')。
  • 命名参数:Npgsql 使用 @param_name;Oracle 使用 :param_name。更新所有参数名前缀。

步骤 6:处理 Oracle 专有 SQL 与 C# 模式

审查内联 SQL 字符串与查询构建器中的 Oracle 专有构造并替换:

  • ROWNUM <= n → LIMIT n
  • ROWNUM = 1 → LIMIT 1
  • NVL(x, y) → COALESCE(x, y)
  • DECODE(expr, v1, r1, ...) → CASE WHEN expr = v1 THEN r1 ... END
  • SYSDATE / SYSTIMESTAMP → NOW() 或 CURRENT_TIMESTAMP
  • TO_CHAR(date, fmt) → TO_CHAR(date, fmt)(大体兼容,需验证格式字符串)
  • TO_DATE(str, fmt) → TO_DATE(str, fmt)(需验证格式字符串)
  • TO_NUMBER(str) → CAST(str AS NUMERIC) 或 str::NUMERIC
  • DUAL 表 → 移除 FROM DUAL;PostgreSQL 可直接求值 SELECT expr
  • CONNECT BY 层次查询 → 重写为递归 CTE(WITH RECURSIVE)
  • MERGE INTO → 重写为 INSERT ... ON CONFLICT DO UPDATE
  • 空字符串 '' 作为 NULL:Oracle 将 '' 视为 NULL,PostgreSQL 不会——检查比较与 IS NULL 守卫
  • VARCHAR2 → VARCHAR 或 TEXT

步骤 7:构建并验证

处理完所有清单项后:在 .Postgres 项目上运行 dotnet build,修复剩余编译错误;搜索确认无 Oracle 专有命名空间残留(Oracle.ManagedDataAccess、OracleConnection、OracleCommand、:param 模式);在 Reports/{ProjectName}/MigrationChecklist.md 中标记已完成项。

EF Core 项目

如果项目使用 Oracle.EntityFrameworkCore:将 DbContext 配置中的提供程序注册 .UseOracle(...) 替换为 .UseNpgsql(...);替换 OracleDbContextOptionsBuilder 引用;审查 OnModelCreating 中的 Oracle 专有配置(如 HasColumnType("NUMBER") → HasColumnType("numeric"));序列配置 modelBuilder.HasSequence<int>("seq_name").StartsAt(1).IncrementsBy(1) 语法兼容,需验证引用序列的列默认值。不要运行 EF Core 迁移——架构由 DDL 脚本(Phase 4)外部管理。

关键约束

仅在 .Postgres 副本内工作,绝不修改原始面向 Oracle 的项目;保持既有 .NET 与 C# 版本,不引入更新的语言或运行时特性;保留注释与应用逻辑,只做 PostgreSQL 兼容性所需的必要改动;Oracle 为事实来源——行为差异须记录为 bug 报告,不得静默更改。