Generate EF Core migrations for the OwlNet dual-provider setup (SqlServer + Sqlite). Covers the correct build, generate, and snapshot workflow to avoid name collisions and snapshot overwrites.
Guide the generation of EF Core migrations for both SqlServer and Sqlite providers in the OwlNet project, which keeps both sets of migrations in the same assembly (OwlNet.Infrastructure) using conditional compilation.
ApplicationDbContext in src/OwlNet.Infrastructure/Persistence/DesignTimeDbContextFactory reads the provider from CLI args (-- SqlServer) or the EF_PROVIDER environment variable. Defaults to Sqlite.OwlNet.Infrastructure.csproj reads $(EF_PROVIDER) and excludes the OTHER provider's migration files from compilation to prevent [Migration] name and ModelSnapshot class collisions.Persistence/Migrations/SqlServer/Persistence/Migrations/Sqlite/EF Core scans the compiled assembly for [Migration] attributes. You MUST build with EF_PROVIDER set so that only the target provider's migration files are compiled:
# For Sqlite
export EF_PROVIDER=Sqlite && dotnet build OwlNet.sln --no-incremental --no-restore
# For SqlServer
export EF_PROVIDER=SqlServer && dotnet build OwlNet.sln --no-incremental --no-restore
--no-incremental is required because MSBuild may cache a previous build that included the other provider's files.
--no-buildUse --no-build to ensure dotnet ef uses the assembly you just built (and doesn't rebuild without the env var):
# Sqlite
export EF_PROVIDER=Sqlite && dotnet ef migrations add <MigrationName> \
--project src/OwlNet.Infrastructure \
--startup-project src/OwlNet.Web \
--output-dir Persistence/Migrations/Sqlite \
--no-build \
-- Sqlite
# SqlServer
export EF_PROVIDER=SqlServer && dotnet ef migrations add <MigrationName> \
--project src/OwlNet.Infrastructure \
--startup-project src/OwlNet.Web \
--output-dir Persistence/Migrations/SqlServer \
--no-build \
-- SqlServer
Each provider folder must contain:
<timestamp>_<MigrationName>.cs — the migration Up/Down methods<timestamp>_<MigrationName>.Designer.cs — model metadataApplicationDbContextModelSnapshot.cs — current model snapshotVerify column types are correct for each provider:
nvarchar(...), bit, int, datetimeoffsetTEXT, INTEGEREF Core locates the ApplicationDbContextModelSnapshot.cs file on the filesystem (not just in the compiled assembly). When generating for the second provider, it may find the first provider's snapshot file and overwrite it with the second provider's model.
Generate migrations for both providers using this exact sequence:
*.cs files from both Migrations/SqlServer/ and Migrations/Sqlite/ (keep .gitkeep).export EF_PROVIDER=Sqlite
dotnet build OwlNet.sln --no-incremental --no-restore
dotnet ef migrations add <Name> --project src/OwlNet.Infrastructure --startup-project src/OwlNet.Web --output-dir Persistence/Migrations/Sqlite --no-build -- Sqlite
This creates the migration + snapshot in Migrations/Sqlite/.export EF_PROVIDER=SqlServer
dotnet build OwlNet.sln --no-incremental --no-restore
dotnet ef migrations add <Name> --project src/OwlNet.Infrastructure --startup-project src/OwlNet.Web --output-dir Persistence/Migrations/SqlServer --no-build -- SqlServer
Migrations/Sqlite/ApplicationDbContextModelSnapshot.cs still has TEXT/INTEGER types (not nvarchar). If it was overwritten, restore it from the Sqlite Designer file (copy BuildTargetModel content into BuildModel).The snapshot content is identical to the Designer file's BuildTargetModel method, but renamed to BuildModel. You can manually recreate it:
Sqlite/<timestamp>_InitialCreate.Designer.cs)InitialCreate to ApplicationDbContextModelSnapshot: ModelSnapshotBuildTargetModel to BuildModel[Migration("...")] attribute (keep [DbContext(...)])ApplicationDbContextModelSnapshot.cs in the correct provider folderUse this skill whenever you need to: