Chapter 21 · PostgreSQL, MySQL, and Cross-Provider Portability

Provider Ecosystem Awareness: Npgsql, MySQL/MariaDB Providers, Versions, and Compatibility Contracts

Turn provider choice into a versioned engineering contract by comparing Npgsql, Oracle MySQL, Pomelo/MariaDB support, package constraints, licenses, database versions, and reproducible provider test matrices.

Advanced170–220 minutesprovider compatibility matrix labEF Core 10.0.11 · Npgsql EF provider 10.0.3 · Oracle MySql.EntityFrameworkCore 10.0.9 · PostgreSQL 18.6 · MySQL 8.4 LTS · .NET 10.0.11 · SDK 10.0.400Free local PostgreSQL/MySQL path · MariaDB/Pomelo EF9 observation onlyProvider compatibility reviewed: August 27, 2026

Learning outcomes

01

Treat an EF Core provider as a versioned compatibility layer with its own release cadence, dependencies, maintainer, license, and database support matrix.

02

Pin the current stable EF Core 10 provider versions for PostgreSQL and Oracle MySQL instead of assuming a matching major version exists.

03

Explain why Pomelo 9.0.0 can run on .NET 10 yet still is not an EF Core 10 provider, and keep its MariaDB path separate from the EF10 mandatory lab.

04

Build a provider smoke-test matrix that proves connection, model construction, migrations, representative queries, writes, transactions, concurrency, and provider-specific features.

05

Record database server versions and provider-generated SQL so restore/build success is never mistaken for runtime compatibility.

06

Keep all mandatory work local/free and avoid embedding credentials in source or generated artifacts.

1. The problem: “EF Core 10” is not a provider compatibility guarantee

ServiceHub now has a mature EF Core model that has been exercised against SQLite and an isolated SQL Server lab. A customer asks for PostgreSQL, another asks for MySQL, and a third runs MariaDB. The tempting response is to switch UseSqlite to another Use... call and assume EF normalizes the rest. That is exactly the portability mistake this chapter prevents.

Provider major versions are contracts, not decorations

EF Core providers plug into internal and public EF services. A package can target .NET 10 and still be constrained to EF Core 9. Always inspect the provider package dependency range, maintainer documentation, supported database versions, and the exact provider build used in tests.

Layer Question to pin Example for this chapter
EF runtime/tooling Which EF patch and dotnet-ef? 10.0.11
Provider Which package/patch and dependency range? Npgsql 10.0.3; Oracle MySQL 10.0.9
ADO.NET driver Which driver is underneath? Npgsql / MySql.Data as selected by provider
Database Which server major/minor? PostgreSQL 18.6; MySQL 8.4 LTS
Topology Local container, VM, managed service? Free local container/instance for mandatory labs

2. Freeze the current 2026 compatibility matrix before writing provider code

At this chapter-generation checkpoint, Microsoft EF Core and dotnet-ef are 10.0.11. Npgsql has a stable EF Core 10 line at 10.0.3. Oracle publishes MySql.EntityFrameworkCore 10.0.9 with EF Core 10 support. Pomelo’s latest stable release is still 9.0.0, whose NuGet dependency range is EF Core 9.x; its EF Core 10 milestone/work is active but not a stable 10.0 release.

Provider package Maintainer / license Stable compatibility used here Database path
Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 Npgsql community · PostgreSQL License EF Core 10 / .NET 10 PostgreSQL 18.6 local
MySql.EntityFrameworkCore 10.0.9 Oracle/MySQL · GPL-2.0 + Universal FOSS Exception EF Core 10; current Oracle provider line MySQL 8.4 LTS local
Pomelo.EntityFrameworkCore.MySql 9.0.0 Pomelo Foundation · MIT EF Core 9.x, .NET 8+; can run on .NET 10 but is not EF10 MySQL/MariaDB observation path only
Do not “force” Pomelo 9 into an EF Core 10 graph

Its published package constrains Microsoft.EntityFrameworkCore.Relational to the 9.0.x line. Ignoring or overriding the constraint is unsupported experimentation, not a production compatibility strategy. For MariaDB-specific Pomelo exercises, use a separate EF9 project or wait for a stable EF10 Pomelo release and re-run this matrix.

3. Build separate provider host projects around one domain contract

The safest learning topology keeps the portable ServiceHub domain/model conventions visible but isolates provider packages so their extension methods, migrations, and transitive drivers do not fight inside one project. The domain is shared; provider hosts are replaceable integration edges.

text · solution shape
src/  ServiceHub.Domain/  ServiceHub.Persistence.Core/  ServiceHub.Persistence.Postgres/  ServiceHub.Persistence.MySql/tests/  ServiceHub.ProviderTests.Postgres/  ServiceHub.ProviderTests.MySql/  ServiceHub.ProviderContracts/
bash · pin the stable EF10 provider packages
dotnet add src/ServiceHub.Persistence.Postgres package Npgsql.EntityFrameworkCore.PostgreSQL --version 10.0.3dotnet add src/ServiceHub.Persistence.MySql package MySql.EntityFrameworkCore --version 10.0.9dotnet add src/ServiceHub.Persistence.Postgres package Microsoft.EntityFrameworkCore.Design --version 10.0.11dotnet add src/ServiceHub.Persistence.MySql package Microsoft.EntityFrameworkCore.Design --version 10.0.11dotnet tool update --local dotnet-ef --version 10.0.11

Keep Microsoft.EntityFrameworkCore and Microsoft design/tool packages at 10.0.11; provider packages are allowed to have independent patch numbers because they release independently.

4. Provider configuration is executable evidence

Construct the same context model through provider-specific options and immediately print provider/model facts. This catches “wrong provider loaded” and model-service replacement problems before a migration or request does real work.

csharp · PostgreSQL and MySQL factories
public static DbContextOptions<ServiceHubContext> PostgresOptions(string cs)    => new DbContextOptionsBuilder<ServiceHubContext>()        .UseNpgsql(cs, o => o.SetPostgresVersion(18, 0))        .EnableDetailedErrors()        .Options;public static DbContextOptions<ServiceHubContext> MySqlOptions(string cs)    => new DbContextOptionsBuilder<ServiceHubContext>()        .UseMySQL(cs)        .EnableDetailedErrors()        .Options;
csharp · runtime provider fingerprint
await using var db = new ServiceHubContext(options);Console.WriteLine($"Provider={db.Database.ProviderName}");Console.WriteLine($"ModelEntities={db.Model.GetEntityTypes().Count()}");Console.WriteLine(db.WorkOrders.Where(w => w.Id > 0).Take(1).ToQueryString());

ProviderName, runtime model metadata, and one translated query form a small but useful fingerprint. They do not prove all features work; they prove the expected provider actually constructed the model and translated at least one representative query.

5. Database server version belongs in the test record

Provider features can branch on database server version. PostgreSQL 18 adds capabilities that earlier majors lack; MySQL has LTS and Innovation release tracks. Capture the database-reported version alongside the package graph so failures are reproducible.

sql · server version probes
-- PostgreSQLSELECT version();SHOW server_version;-- MySQLSELECT VERSION();SELECT @@version, @@version_comment, @@collation_server, @@character_set_server;

Do not assume a managed service, local Docker image, or developer workstation has the same minor version as CI. Store the result in test output, not secrets.

6. A provider test matrix is more valuable than a provider abstraction slogan

Portability means a defined set of behaviors is tested on every supported provider. A useful matrix starts with shared semantics and then adds provider-specific rows. It should not require every engine to support every feature.

Contract area PostgreSQL test MySQL test Failure means
Model boot context builds; expected provider metadata same package/model incompatibility
Migrations create/apply/rollback disposable schema same DDL/provider gap
Core queries filters, ordering, paging, aggregates same translation/semantics drift
Writes generated key + concurrency + transaction same store-generation/write gap
JSON/search/native types Npgsql-specific tests MySQL-specific tests capability adapter regression
Plan/index evidence EXPLAIN/ANALYZE as safe EXPLAIN performance portability issue

7. Failure case: package restore succeeds, so the provider must be compatible

A developer upgrades the application to EF Core 10, leaves a provider on an EF9-only stable package, suppresses dependency warnings, and sees a successful build. At runtime model creation or query translation can fail because provider services target a different EF contract.

bash · treat package graph warnings as test failures
dotnet list src/ServiceHub.Persistence.Postgres package --include-transitivedotnet list src/ServiceHub.Persistence.MySql package --include-transitivedotnet restore --force-evaluate# CI policy: fail on package downgrade/version-range warnings.

Repair: use a provider version whose declared dependency range includes the chosen EF major. If a stable provider does not exist, either stay on the compatible EF major for that provider in an isolated service/project or choose a supported provider. Do not convert a compatibility gap into a hidden runtime experiment.

8. Secrets, local containers, and free reproducibility

Mandatory labs use local PostgreSQL and MySQL instances/containers with disposable databases. Connection strings come from environment variables or user secrets; no production hostname, password, cloud key, or certificate is written into lesson source.

bash · environment-only examples
# Bash / PowerShell equivalents are fine; values are local lab credentials only.export SERVICEHUB_PG="Host=localhost;Port=5432;Database=servicehub_pg_lab;Username=servicehub_lab;Password=..."export SERVICEHUB_MYSQL="Server=localhost;Port=3306;Database=servicehub_mysql_lab;User=servicehub_lab;Password=..."
Free/local path

PostgreSQL Community and MySQL Community are sufficient. MariaDB may be tested separately, but the stable Pomelo path is currently EF Core 9, so it is not a mandatory EF Core 10 lab in this chapter.

9. Mandatory lab: produce a compatibility evidence packet

  1. Record dotnet --info, dotnet ef --version, direct/transitive package graphs, and the current provider package versions.
  2. Start disposable PostgreSQL 18.6 and MySQL 8.4 LTS databases or equivalent local installations. Record version()/VERSION().
  3. Construct ServiceHubContext once with Npgsql and once with Oracle MySQL. Print Database.ProviderName and model entity count.
  4. Apply a provider-specific baseline migration to an empty disposable database for each engine; inspect the DDL before applying.
  5. Seed the same minimal ServiceHub rows and run five shared contract queries/writes.
  6. Capture ToQueryString() and command logs for at least one query per provider.
  7. Create a matrix row for Pomelo 9.0.0 showing its EF Core 9 dependency constraint and mark EF Core 10 stable support as unavailable at this checkpoint rather than trying to force-install it.
  8. Drop the disposable databases after evidence is captured.

10. Production judgment and bridge

Provider portability begins with release engineering: package compatibility, database versions, support posture, license, and a repeatable test matrix. A provider is not “just a driver,” and package restore is not evidence of semantic portability. Lesson 2 moves from ecosystem compatibility into the physical type systems and value-generation features that differ even when the same ServiceHub CLR model compiles unchanged.

Check your understanding

  1. Why can a package target .NET 10 yet still not be an EF Core 10 provider?
  2. Which stable provider versions are used for the mandatory EF Core 10 PostgreSQL/MySQL labs?
  3. Why is Pomelo/MariaDB not a mandatory EF Core 10 lab here?
  4. What three runtime facts should accompany a provider query failure?
  5. What does a cross-provider contract test prove?
  6. Why isolate provider hosts/projects?
Review the answers

1. The target framework and the Microsoft.EntityFrameworkCore dependency range are separate contracts; Pomelo 9 targets compatible .NET runtimes but depends on EF Core 9.x.

2. Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 and Oracle MySql.EntityFrameworkCore 10.0.9.

3. Pomelo’s latest stable package is 9.0.0 and declares EF Core 9.x compatibility; EF10 work is ongoing but not a stable 10.0 release at this checkpoint.

4. Exact EF/provider/driver package graph, database server version, and generated SQL/log/provider name.

5. A defined business/query/write behavior works on each supported provider; it does not prove every provider has identical SQL or feature sets.

6. It keeps provider extensions, migration assemblies, drivers and dependency constraints from contaminating the portable domain/core model and makes failures attributable.

Authoritative references

Provider compatibility must be verified from current provider/package documentation, not inferred from EF Core branding.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only Ethereum or ERC-20 compatible assets to this address.

\n