Unload target assemblies before publishing the migrations bundle#38456
Unload target assemblies before publishing the migrations bundle#38456ajcvickers wants to merge 2 commits into
Conversation
Fixes dotnet#25555 `dotnet ef migrations bundle` loads the target and startup assemblies into the tool process to discover the DbContext, then shells out to `dotnet publish`, which rebuilds and overwrites those same bin\...\*.dll files. On Windows a loaded assembly file is locked, so the copy fails with MSB3027/MSB3021 ("being used by another process"). The assemblies were loaded into the non-collectible default AssemblyLoadContext, so they could never be released while the tool process was alive. Load them into a collectible AssemblyLoadContext instead and unload it when the executor is disposed (before publish runs), releasing the files.
|
/cc @bricelam, @AndriySvyryd. For more information, see the analysis on #25555. |
|
Note I'm going to do Windows validation on this before taking this out of draft. I don't dev on Windows anymore, so it may take a bit of time to setup. |
The recursive temp-dir delete in the test's finally ran immediately after the load context was unloaded. On Windows the OS can release the assembly's file handle slightly after the context is collected, so the delete intermittently threw UnauthorizedAccessException and masked the (passing) assertion. Unix allows deleting a file with an open handle, so this only surfaced on Windows CI. Retry the cleanup briefly and give up quietly; it is a test-cleanup concern only (in the real tool, publish runs much later).
There was a problem hiding this comment.
Pull request overview
This PR addresses a Windows-specific failure in dotnet ef migrations bundle where the tool process keeps target/startup assemblies loaded (and therefore file-locked) while subsequently running dotnet publish, which tries to overwrite those same assemblies. The intended fix is to load user assemblies into a collectible AssemblyLoadContext and unload it when the executor is disposed (before publish runs).
Changes:
- Update
ReflectionOperationExecutorto create a collectibleAssemblyLoadContextand unload it duringDispose(). - Add a regression test to verify that the target assembly is no longer loaded after disposing the executor.
- Update
ef.Testsproject settings/references to support the new test scenario (preserved compilation context and assembly aliasing).
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/ef/ReflectionOperationExecutor.cs |
Introduces collectible ALC creation and explicit unload logic in Dispose() to release user assemblies. |
test/ef.Tests/ReflectionOperationExecutorTest.cs |
Adds a regression test asserting the target assembly is unloaded after executor disposal. |
test/ef.Tests/ef.Tests.csproj |
Enables PreserveCompilationContext (needed by BuildSource/DependencyContext) and adds an alias for the ef tool project reference for the new test. |
| protected override void Execute(string operationName, object resultHandler, IDictionary arguments) | ||
| => Activator.CreateInstance( | ||
| _commandsAssembly.GetType(ExecutorTypeName + "+" + operationName, throwOnError: true, ignoreCase: true)!, | ||
| _executor, | ||
| resultHandler, |
Fixes #25555
dotnet ef migrations bundleloads the target and startup assemblies into the tool process to discover the DbContext, then shells out todotnet publish, which rebuilds and overwrites those same bin...*.dll files. On Windows a loaded assembly file is locked, so the copy fails with MSB3027/MSB3021 ("being used by another process"). The assemblies were loaded into the non-collectible default AssemblyLoadContext, so they could never be released while the tool process was alive.Load them into a collectible AssemblyLoadContext instead and unload it when the executor is disposed (before publish runs), releasing the files.