ef6
Makes an Entity Framework 6 application dual-provider for the dualdb tool - DbConfiguration and provider registration, connection injection, two migration lineages with distinct ContextKeys, and the .
SKILL.mdskills/ef6/SKILL.md
GitHubEF6
EF6 is harder than EF Core in one specific way: provider registration failures surface at run time, not at build. The application compiles and then throws "No Entity Framework provider found" on the first query.
When to use
Any DbContext from System.Data.Entity, DbConfiguration,
DbMigrationsConfiguration, Database.SqlQuery, ExecuteSqlCommand, .edmx,
<entityFramework> or <DbProviderFactories> config sections.
Decision procedure
- Add
EntityFramework6.Npgsql. Register both providers - viaDbConfiguration(preferred) or via<entityFramework>plus<DbProviderFactories>(PG-ORM-008). - Give the context a constructor taking a
DbConnectionsupplied by the factory, rather than a connection-string name. A string name re-couples the context to one provider. - Two migration lineages:
Migrations/SqlServer/andMigrations/PostgreSql/, with distinctDbMigrationsConfigurationtypes and distinctContextKeys (PG-ORM-004).__MigrationHistoryrecords the ContextKey, so sharing one makes each provider think the other's migrations are already applied. - Audit
HasColumnType("datetime2"|"varchar"|"money"|"uniqueidentifier")andHasMaxLengthon text-ish columns (PG-ORM-002). - Review
HasDatabaseGeneratedOptionandIsRowVersion()(PG-TYPE-035). - Treat
Database.SqlQueryandExecuteSqlCommandas raw SQL (PG-ORM-007). - If there is an
.edmx:manual-review(PG-ORM-009). There is no partial fix.
Rule table
<!-- dualdb:rules:begin categories=ef6 --> <!-- generated from knowledge/rules/*.yaml by `dualdb rules sync` - do not edit -->| id | SQL Server | PostgreSQL | tier | note |
|---|---|---|---|---|
PG-ORM-008 |
public AppContext() : base("name=AppDb") { } |
public AppContext(DbConnection connection) : base(connection, contextOwnsConnection: tr... |
review | EF6 needs a DbConfiguration and provider registration per provider |
PG-ORM-009 |
Model.edmx with Provider="System.Data.SqlClient" |
Model.SqlServer.edmx and Model.PostgreSql.edmx |
manual | An .edmx model is provider-specific |
Worked examples
1. add-pg-alternate - connection injection
// original, unchanged
public AppContext() : base("name=AppDb") { }
// added
public AppContext(DbConnection connection) : base(connection, contextOwnsConnection: true) { }
The parameterless constructor stays for the SQL Server path and for the migrations tooling, which needs it. The new constructor is what the factory uses. Callers are updated at the composition root only.
2. add-pg-file - a second migration lineage
// Migrations/PostgreSql/Configuration.cs
internal sealed class PostgreSqlConfiguration : DbMigrationsConfiguration<AppContext>
{
public PostgreSqlConfiguration()
{
MigrationsDirectory = @"Migrations\PostgreSql";
ContextKey = "App.Data.AppContext.PostgreSql"; // distinct - see below
SetSqlGenerator("Npgsql", new NpgsqlMigrationSqlGenerator());
}
}
The existing SQL Server configuration is untouched. The distinct ContextKey is
the part that is easy to miss and expensive to get wrong: __MigrationHistory
keys on it, so sharing one means each provider sees the other's rows and skips its
own migrations.
3. manual-review - an .edmx model
The SSDL section of an .edmx describes the store schema in provider terms, so one
.edmx cannot serve two providers (PG-ORM-009). Three options, all projects
rather than edits:
- Two
.edmxfiles. Smallest immediate change, no code rewrite - and two designer-generated models to keep in step by hand, which is the drift risk the tool avoids everywhere else. - Convert to Code First. Removes the problem properly and makes the mapping reviewable; a substantial one-off change to a working system, and Database First conventions do not always survive it.
- Move that path to Dapper or ADO.NET. Best where the
.edmxcovers a small, query-shaped area; unrealistic where it models the domain, and it loses change tracking.
The tool does not choose. It emits the entry with all three and the file list.
Traps
- Registration failures are runtime, not build. The corpus test for EF6 must actually resolve a context per provider, not just compile.
- A shared
ContextKeysilently skips migrations. Nothing errors; the schema is just missing. Database.SqlQuery<T>is raw SQL and reads by column name, so it inherits the casing break (PG-API-001)..edmxis not the only Database First artifact -.dbml(LINQ to SQL) has the same problem and no PostgreSQL provider at all.IsRowVersion()in EF6 fluent config is the samePG-TYPE-035decision as in EF Core, with fewer provider hooks to express it.
Escalate when
- An
.edmxor.dbmlis present (always). DbConfigurationis set by attribute or by<entityFramework codeConfigurationType>and the app has more than one context.- Migrations were generated with
-IgnoreChangesor hand-edited - the two lineages cannot be derived from the model alone.
Do not
- Do not replace the parameterless constructor. Migrations tooling needs it.
- Do not remove the existing SqlClient provider entry when adding Npgsql - both are registered, side by side.
- Do not attempt one migration set for both providers.
- Do not upgrade EF6 to EF Core as part of this port. That is modernization.