Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Migrates .NET/C# data access code from Oracle to PostgreSQL (Npgsql). Replaces Oracle NuGet packages, rewrites OracleConnection/OracleCommand/OracleDataReader usage, fixes DbType mappings, updates stored procedure invocation patterns, and adapts connection string configuration. Use when migrating the application code layer of a .NET project during an Oracle-to-PostgreSQL database migration.
.claude/skills/github-migrating-oracle-to-postgres-data-access-code/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-04 | ✗→✓ | ▲ Improved | 28% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 75% | 0% |
| case-25 | ✗→✓ | ▲ Improved | 48% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 61% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 95% | 0% |
Migrate the C# data access layer of a single .Postgres-copy project from Oracle (Oracle.ManagedDataAccess) to PostgreSQL (Npgsql). Work item by item through Reports/{ProjectName}/MigrationChecklist.md.
.Postgres project copy exists (created in Phase 5 setup).Reports/{ProjectName}/MigrationChecklist.md exists and is the source of truth for what to change.Reports/{ProjectName}/OracleRiskAnalysis.md exists for cross-referencing behavioral differences.Progress:
- [ ] Step 1: Replace NuGet packages
- [ ] Step 2: Update connection string configuration
- [ ] Step 3: Rewrite ADO.NET type references
- [ ] Step 4: Fix DbType mappings
- [ ] Step 5: Migrate stored procedure invocation
- [ ] Step 6: Address Oracle-specific SQL and syntax
- [ ] Step 7: Build and verifyStep 1: Replace NuGet packages
In the .csproj of the .Postgres project:
Oracle.ManagedDataAccess.Core, Oracle.EntityFrameworkCore (and any other Oracle.* packages)Npgsql (for ADO.NET) and/or Npgsql.EntityFrameworkCore.PostgreSQL (for EF Core)System.Data abstractions (IDbConnection, IDbCommand) are used project-wide, the surface-level code may need fewer changes — identify them first.Step 2: Update connection string configuration
appsettings.json, appsettings.{env}.json, web.config, app.config, or environment variable configuration.Host=localhost;Port=5432;Database=mydb;Username=myuser;Password=mypasswordIConfiguration).OracleConnection). Prefer keeping the same key name to minimize application config changes.Step 3: Rewrite ADO.NET type references
Replace Oracle-specific ADO.NET types with Npgsql equivalents:
| Oracle type | Npgsql replacement | |---|---| | OracleConnection | NpgsqlConnection | | OracleCommand | NpgsqlCommand | | OracleDataReader | NpgsqlDataReader | | OracleDataAdapter | NpgsqlDataAdapter | | OracleParameter | NpgsqlParameter | | OracleTransaction | NpgsqlTransaction | | OracleException | NpgsqlException | | OracleDbType | NpgsqlDbType (from NpgsqlTypes namespace) |
Update using directives accordingly (using Oracle.ManagedDataAccess.Client → using Npgsql).
If the codebase uses IDbConnection/IDbCommand abstractions registered via DI, update only the DI registration and connection string — the consuming code may not need changes.
Step 4: Fix DbType and NpgsqlDbType mappings
Oracle parameter types do not map 1:1 to Npgsql. Review every OracleParameter (now NpgsqlParameter) that sets an explicit type:
| Oracle type | Notes | |---|---| | OracleDbType.Varchar2 | Use NpgsqlDbType.Varchar or omit (Npgsql infers from value) | | OracleDbType.Clob | Use NpgsqlDbType.Text | | OracleDbType.Number | Use NpgsqlDbType.Numeric or NpgsqlDbType.Integer depending on precision | | OracleDbType.Date | Use NpgsqlDbType.Date (date only) or NpgsqlDbType.Timestamp (if time component used) | | OracleDbType.TimeStamp | Use NpgsqlDbType.Timestamp | | OracleDbType.RefCursor | Use NpgsqlDbType.Refcursor — see Step 5 | | OracleDbType.Char | Use NpgsqlDbType.Char |
For parameters where Oracle inferred the type from the value, Npgsql also infers — explicit type setting is often unnecessary and can be removed.
Step 5: Migrate stored procedure invocation
Oracle and PostgreSQL stored procedure invocation differ significantly:
CommandType.StoredProcedure for function calls. For procedures that use OUT parameters, PostgreSQL requires CommandType.Text with CALL proc_name(...) syntax in some versions of Npgsql — verify against the target Npgsql version.RETURNS TABLE / RETURNS SETOF, use ExecuteReader() directly — no cursor parameter needed.RETURNS refcursor, call within a transaction, read the cursor name from the output parameter, then issue FETCH ALL IN "<cursor_name>".OracleRefCursor).INOUT or function return values. Verify parameter direction matches the migrated procedure signature.NEXTVAL: Replace SELECT {SEQUENCE}.NEXTVAL FROM DUAL with SELECT nextval('{sequence_name}').@param_name; Oracle used :param_name. Update all parameter name prefixes.Step 6: Address Oracle-specific SQL and C# patterns
Review inline SQL strings and query builders for Oracle-specific constructs and replace:
| Oracle construct | PostgreSQL replacement | |---|---| | 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() or CURRENT_TIMESTAMP | | TO_CHAR(date, fmt) | TO_CHAR(date, fmt) (mostly compatible; verify format strings) | | TO_DATE(str, fmt) | TO_DATE(str, fmt) (verify format strings) | | TO_NUMBER(str) | CAST(str AS NUMERIC) or str::NUMERIC | | || string concat | || (compatible) | | DUAL table | Remove FROM DUAL; PostgreSQL evaluates SELECT expr without a table | | CONNECT BY hierarchy | Rewrite using recursive CTEs (WITH RECURSIVE) | | MERGE INTO | Rewrite as INSERT ... ON CONFLICT DO UPDATE | | Empty string '' as NULL | Oracle treats '' as NULL; PostgreSQL does not — check comparisons and IS NULL guards | | VARCHAR2 | VARCHAR or TEXT |
Step 7: Build and verify
After addressing all checklist items:
dotnet build on the .Postgres project. Fix any remaining compilation errors.Oracle.ManagedDataAccess, OracleConnection, OracleCommand, :param patterns.Reports/{ProjectName}/MigrationChecklist.md.If the project uses Oracle.EntityFrameworkCore:
DbContext configuration: .UseOracle(...) → .UseNpgsql(...)OracleDbContextOptionsBuilder references.OnModelCreating for Oracle-specific configurations (e.g., HasColumnType("NUMBER") → HasColumnType("numeric")).modelBuilder.HasSequence<int>("seq_name").StartsAt(1).IncrementsBy(1) syntax is compatible; verify column defaults referencing sequences..Postgres copy — never modify the original Oracle-targeting project.| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-03 | fail→fail | 3,761 | 8,835 | +135% | 1 | 1 | 0% | 225 | 2,189 | +873% | 0 | 0 | — |
case-04 | fail→pass | 24,767 | 11,369 | -54% | 1 | 1 | 0% | 2,449 | 3,126 | +28% | 0 | 0 | — |
case-05 | fail→pass | 10,399 | 10,931 | +5% | 1 | 1 | 0% | 1,654 | 2,888 | +75% | 0 | 0 | — |
case-06 | fail→fail | 23,611 | 19,905 | -16% | 1 | 1 | 0% | 3,385 | 5,604 | +66% | 0 | 0 | — |
case-11 | pass→pass | 11,090 | 4,554 | -59% | 1 | 1 | 0% | 1,037 | 2,812 | +171% | 0 | 0 | — |
case-25 | fail→pass | 12,219 | 4,872 | -60% | 1 | 1 | 0% | 1,887 | 2,794 | +48% | 0 | 0 | — |
case-01 | fail→fail | 9,369 | 14,938 | +59% | 1 | 1 | 0% | 270 | 2,157 | +699% | 0 | 0 | — |
case-02 | fail→fail | 8,314 | 2,452 | -71% | 1 | 1 | 0% | 213 | 2,177 | +922% | 0 | 0 | — |
case-07 | fail→pass | 16,952 | 6,906 | -59% | 1 | 1 | 0% | 2,096 | 3,365 | +61% | 0 | 0 | — |
case-08 | pass→pass | 10,600 | 8,548 | -19% | 1 | 1 | 0% | 1,011 | 2,591 | +156% | 0 | 0 | — |
case-09 | pass→pass | 10,674 | 10,872 | +2% | 1 | 1 | 0% | 976 | 3,054 | +213% | 0 | 0 | — |
case-10 | fail→pass | 8,230 | 4,296 | -48% | 1 | 1 | 0% | 1,431 | 2,787 | +95% | 0 | 0 | — |
case-12 | pass→pass | 9,769 | 3,342 | -66% | 1 | 1 | 0% | 788 | 2,552 | +224% | 0 | 0 | — |
case-13 | pass→pass | 5,303 | 4,662 | -12% | 1 | 1 | 0% | 994 | 2,748 | +176% | 0 | 0 | — |
case-14 | pass→pass | 6,856 | 5,867 | -14% | 1 | 1 | 0% | 1,125 | 3,007 | +167% | 0 | 0 | — |
case-15 | pass→pass | 13,369 | 10,411 | -22% | 1 | 1 | 0% | 2,416 | 3,959 | +64% | 0 | 0 | — |
case-16 | pass→pass | 14,803 | 9,896 | -33% | 1 | 1 | 0% | 2,896 | 3,823 | +32% | 0 | 0 | — |
case-17 | pass→pass | 2,574 | 8,735 | +239% | 1 | 1 | 0% | 406 | 2,583 | +536% | 0 | 0 | — |
case-18 | pass→pass | 7,312 | 5,041 | -31% | 1 | 1 | 0% | 1,228 | 2,880 | +135% | 0 | 0 | — |
case-19 | pass→pass | 13,901 | 5,641 | -59% | 1 | 1 | 0% | 1,614 | 3,003 | +86% | 0 | 0 | — |
case-20 | pass→pass | 6,598 | 8,142 | +23% | 1 | 1 | 0% | 1,046 | 3,405 | +226% | 0 | 0 | — |
case-21 | pass→pass | 11,969 | 11,161 | -7% | 1 | 1 | 0% | 2,137 | 3,851 | +80% | 0 | 0 | — |
case-22 | pass→pass | 10,048 | 9,443 | -6% | 1 | 1 | 0% | 2,020 | 3,845 | +90% | 0 | 0 | — |
case-23 | pass→pass | 7,152 | 9,331 | +30% | 1 | 1 | 0% | 1,589 | 3,798 | +139% | 0 | 0 | — |
case-24 | fail→pass | 10,465 | 4,828 | -54% | 1 | 1 | 0% | 1,638 | 2,686 | +64% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted, and 22 counted toward the lift figure. The other 3 produced results that are not comparable between the two arms, so they are excluded from the headline rather than averaged into it. The headline lift of +24 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.