-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Add runtime migration creation and application #37415
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
a15611a to
9a35a9b
Compare
62018f1 to
5c41f2f
Compare
Note on SQL Server Integration TestsThe Why these tests are skipped in CI:
Test coverage is still maintained:
This is consistent with how other complex migration infrastructure tests handle CI limitations. |
test/EFCore.SqlServer.FunctionalTests/Migrations/RuntimeMigrationSqlServerTest.cs
Outdated
Show resolved
Hide resolved
src/EFCore.Design/Design/DesignTimeServiceCollectionExtensions.cs
Outdated
Show resolved
Hide resolved
src/EFCore.Design/Migrations/Design/IDynamicMigrationsAssembly.cs
Outdated
Show resolved
Hide resolved
src/EFCore.Design/Migrations/Design/IDynamicMigrationsAssembly.cs
Outdated
Show resolved
Hide resolved
|
Thank you for the thorough review @AndriySvyryd! I've addressed all your feedback:
All tests pass locally (SQLite: 26 tests, SQL Server: 7 tests, CSharpMigrationCompiler: 4 tests, MigrationsOperations: 2 tests). |
src/EFCore.Relational/Extensions/RelationalDatabaseFacadeExtensions.cs
Outdated
Show resolved
Hide resolved
| OutputDirectory = outputDir, | ||
| Namespace = @namespace, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Create default values for OutputDirectory and Namespace if not specified, like in AddMigration above. In general, try combine this and AddMigration into a single method.
| /// See <see href="https://aka.ms/efcore-docs-migrations">Database migrations</see> for more information and examples. | ||
| /// </para> | ||
| /// </remarks> | ||
| public class RuntimeMigrationService : IRuntimeMigrationService |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't create a new service for this, distribute the functionality among the existing services like IMigrationsScaffolder and IMigrator,
|
Addressed the additional review feedback:
Files changed: 11 files, +132/-2064 lines (significant cleanup) |
test/EFCore.Design.Tests/Design/Internal/MigrationsOperationsTest.cs
Outdated
Show resolved
Hide resolved
In Helix distributed testing, PIPELINE_WORKSPACE is not set. Add HELIX_CORRELATION_PAYLOAD check so tests marked with [SqlServerCondition(IsNotCI)] are properly skipped in Helix.
New SQLite integration tests: - Can_apply_multiple_sequential_migrations: Apply 2 migrations in sequence - Can_create_migration_with_indexes_and_foreign_keys: Complex model with FK/indexes - Generated_migration_file_has_correct_structure: Verify file contents - Down_migration_reverses_changes: Verify rollback works New CLI/OperationExecutor tests: - CreateAndApplyMigration_errors_for_bad_names: Invalid migration name handling - CreateAndApplyMigration_errors_for_empty_name: Empty name validation
- Add RevertMigration and RevertMigrationAsync methods to IRuntimeMigrationService interface - Implement revert functionality in RuntimeMigrationService that: - Tracks applied dynamic migrations - Executes Down operations to undo changes - Removes migration from history table - Add error handling for missing/not-found migrations - Fix tests to properly verify actual rollback behavior instead of just checking generated code - Add tests for error cases and async revert
- Add partial failure tests (table conflict, external interference) - Add error recovery tests (revert after manual drop) - Add dry run safety verification - Add migration tracking and result verification tests - Add edge case tests for special characters and null paths - Total: 26 SQLite functional tests, 20 design tests (46 total)
Testing for transient CI failures.
Add validation for invalid file name characters and context name conflicts to the CreateAndApplyMigration operation, matching the existing validation in the AddMigration operation. This fixes the Windows CI test failure where tests for bad migration names were expecting proper error handling.
- Rename CreateAndApplyMigration to AddAndApplyMigration - Add CLI validation: error if -o or -n used without --add - Extract common migration name validation into helper methods - Merge IDynamicMigrationsAssembly into IMigrationsAssembly - Replace CompiledMigration with AddMigrations(Assembly) API - Fix SQL Server tests: use static DB name, remove EnsureDeleted - Remove CompiledMigration, IDynamicMigrationsAssembly, DynamicMigrationsAssembly
- Move validation to Validate() override in DatabaseUpdateCommand - Remove CreateAndApplyMigration extension methods from RelationalDatabaseFacadeExtensions - Delete IRuntimeMigrationService, RuntimeMigrationOptions, RuntimeMigrationResult - Delete RuntimeMigrationService - functionality moved to MigrationsOperations - Refactor AddAndApplyMigration to: - Use SubnamespaceFromOutputPath for defaults (like AddMigration) - Skip migration creation if no pending model changes - Return MigrationFiles instead of RuntimeMigrationResult - Add RevertMigration method to MigrationsOperations - Remove functional tests that depended on deleted API
Add tests for scaffolding, compiling, registering, and applying migrations at runtime. These tests run for all providers that implement DesignTimeTestBase (SQLite and SQL Server).
Add tests that were missing from the original implementation: - HasPendingModelChanges_returns_false_after_migration - Compiled_migration_contains_correct_operations - Can_scaffold_and_save_migration_to_disk
Add 21 new tests across multiple test files: CSharpMigrationCompilerTest.cs (5 tests): - CompileMigration_throws_on_null_migration - CompileMigration_throws_on_null_context_type - CompileMigration_throws_on_empty_migration_code - CompileMigration_handles_unicode_in_migration_name - CompileMigration_throws_on_very_long_migration_name MigrationsOperationsTest.cs (4 tests): - AddMigration_validates_migration_name_with_invalid_characters - AddMigration_rejects_context_class_name - RevertMigration_throws_when_no_dynamic_migrations - RevertMigration_throws_when_specifying_migration_id_with_empty_list DesignTimeTestBase.cs (5 tests): - Can_apply_multiple_migrations_sequentially - Migration_up_and_down_operations_are_symmetric - Can_revert_migration_using_down_operations - Applied_migration_is_recorded_in_history - Compiled_migration_has_matching_up_and_down_table_operations
- Add empty name validation to CreateAndApplyMigration - Create separate RuntimeMigrationTestBase.cs for runtime migration tests - Create RuntimeMigrationSqliteTest.cs with proper test isolation - Revert DesignTimeTestBase.cs to original state (only 2 tests) - Use fixed database name with sequential test execution to prevent conflicts
- Add GetDatabaseModel helper using IDatabaseModelFactory for schema introspection - Add ScaffoldAndApplyMigration helper to reduce boilerplate - Add 9 new rigorous tests that verify actual database schema: - Migration_creates_correct_table_structure - Migration_creates_correct_primary_keys - Migration_creates_correct_foreign_keys (with cascade delete verification) - Migration_creates_columns_with_correct_constraints - Migration_down_removes_schema_completely - Migration_creates_foreign_key_index - Migration_with_no_changes_produces_empty_operations - Migration_preserves_existing_data - Applied_migration_snapshot_matches_model Total: 22 runtime migration tests now verify scaffolding, compilation, application, schema structure, rollback, and data preservation.
EF Core's compliance test requires all test base classes to have implementations for each provider. This adds the SQL Server implementation of RuntimeMigrationTestBase to fix the CI failure.
Clear SQLite connection pools before deleting database to release file locks. This prevents "file in use" errors on Windows CI.
- CLI: Make --add a switch, use <MIGRATION> argument for name - CLI: Add --json option for machine-readable migration output - CLI: Add validation requiring migration name when using --add - PowerShell: Add -Add, -OutputDir, -Namespace params to Update-Database - Remove RevertMigration method and _appliedDynamicMigrationIds tracking - Move IMigrationCompiler/CSharpMigrationCompiler to Internal namespace - Update tests: use table cleanup instead of database deletion - Update tests: assert specific exception messages
- Extract PrepareForMigration helper to consolidate common validation between AddMigration and AddAndApplyMigration methods - Simplify CSharpMigrationCompiler by removing arbitrary assembly prefix filtering - now includes all non-dynamic assemblies as reviewer suggested - Add RemoveMigration_removes_dynamically_created_migration test that exercises the full lifecycle: scaffold -> save -> compile -> register -> apply -> revert -> remove
Per PR review comment 2663355462: Move the Validate() method from DatabaseUpdateCommand.cs to DatabaseUpdateCommand.Configure.cs so the validation is also included in the dotnet-ef tool. Added required resource strings to dotnet-ef: - MissingArgument - OutputDirRequiresAdd - NamespaceRequiresAdd
Override CleanDatabase in RuntimeMigrationSqlServerTest to properly handle foreign key constraints. SQL Server requires dropping FK constraints before dropping tables, unlike SQLite which handles this automatically with DROP TABLE IF EXISTS. The fix: 1. Drops all foreign key constraints first using dynamic SQL 2. Then drops all tables 3. Finally drops the migrations history table
Properly restore connection state after cleaning database tables. The connection is closed after cleanup only if it wasn't already open before, preventing "connection was not closed" errors in tests that expect to open the connection themselves.
1. AddAndApplyMigration error handling: - Add try-catch around scaffold-compile-apply chain - Clean up saved files on failure to prevent orphans - Add TryDeleteFile helper for best-effort cleanup - Add AddAndApplyMigrationFailed resource string 2. Context disposal in PrepareForMigration: - Wrap context usage in try-catch - Dispose context if validation or service building fails - Prevents context leaks on validation exceptions 3. Thread safety in MigrationsAssembly: - Add lock protection around _additionalAssemblies, _migrations, and _modelSnapshot - Protect Migrations property getter, ModelSnapshot property getter, and AddMigrations method - Prevents race conditions in multi-threaded scenarios
The snapshot file may have overwritten an existing snapshot during Save(). Deleting it on failure would leave the project without a snapshot, breaking future migrations. Only delete migration and metadata files which are always newly created.
- Remove file deletion on failure (keep files for debugging) - Inline validation methods into PrepareForMigration - Remove DisableParallelization from test classes - Refactor tests to use SharedStoreFixtureBase pattern - Use NonCapturingLazyInitializer for MigrationsAssembly.Migrations - Convert to using declarations to reduce nesting - Make CleanDatabase virtual for provider overrides - Fix thread safety with lock-based ModelSnapshot caching
f3a91b0 to
cd324fd
Compare
Question on NonCapturingLazyInitializer for Migrations PropertyPer review comment, I switched The Issue
Current Implementation// Migrations - uses NonCapturingLazyInitializer (per review request)
public virtual IReadOnlyDictionary<string, TypeInfo> Migrations
=> NonCapturingLazyInitializer.EnsureInitialized(
ref _migrations,
this,
static self => { /* compute */ });
// AddMigrations - invalidates cache
public virtual void AddMigrations(Assembly assembly)
{
lock (_lock)
{
_additionalAssemblies.Add(assembly);
_migrations = null; // <-- Race: can be overwritten by concurrent EnsureInitialized
_modelSnapshot = null;
_modelSnapshotInitialized = false;
}
}Options
Practical Risk AssessmentThe race is unlikely because:
Please advise on preferred approach. Happy to adjust either way. |
- Move migration name validation before context creation in AddMigration and AddAndApplyMigration to ensure proper error messages when name is empty - Use Single() instead of First() in Migration_preserves_existing_data test to avoid FirstWithoutOrderByAndFilterWarning
Replace First() with Single() to avoid FirstWithoutOrderByAndFilterWarning
Close connection before migrator.Migrate("0") call and reopen after,
since the migrator manages its own connection state internally.
Summary
Implements #37342: Allow creating and applying migrations at runtime without recompiling.
This adds support for creating and applying migrations at runtime using Roslyn compilation, enabling scenarios like .NET Aspire and containerized applications where recompilation isn't possible.
CLI Usage
The
-o/--output-dir,-n/--namespace, and--jsonoptions require--addto be specified.PowerShell Usage
Architecture
IMigrationCompiler/CSharpMigrationCompilerIMigrationsAssembly.AddMigrations(Assembly)MigrationsOperations.AddAndApplyMigration()Design Decisions
IMigrationsScaffolderfor scaffolding andIMigratorfor applying, adding only the newIMigrationCompilerserviceIMigrationsAssemblyinterface to accept additional assemblies containing runtime-compiled migrationsAddMigration, files are always saved to enable source control and future recompilationIMigrationCompilerandCSharpMigrationCompilerare in the.Internalnamespace as they require design work for public APIMigrationsAssemblyuses locking to protect against race conditions when adding migrations concurrentlyWorkflow
Robustness Features
AddAndApplyMigrationwraps the save-compile-register-apply chain in try-catch, deleting saved files on failure to prevent orphansPrepareForMigrationensures the DbContext is disposed if validation or service building failsMigrationsAssemblyuses locking to protect shared state (migrations dictionary, model snapshot, additional assemblies list)Limitations
[RequiresDynamicCode]Test plan
CSharpMigrationCompilerMigrationsOperations.AddAndApplyMigrationRuntimeMigrationTestBase(SQLite and SQL Server implementations)Fixes #37342