用途
本技能用于将单个 .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 报告,不得静默更改。