I recently made the switch from Visual Studio to JetBrains Rider for my .NET Core web application projects.
Whilst I am very pleased with the new development experience, there was one issue that I had to overcome.
The problem
When building a solution where I was running the web application in a locally hosted IIS instance, the build often failed because of the error below:
Microsoft.Common.CurrentVersion.targets(4685, 5): [MSB3027] Could not copy "obj\Debug\net7.0\foo.dll" to "bin\Debug\net7.0\foo.dll". Exceeded retry count of 10. Failed. The file is locked by: "IIS Worker Process (10592)
This was because the IIS Worker Process
had locked the DLL files in the bin
directory, causing the build to fail when it attempted to overwrite them.
The solution
The solution is to stop the application pool for the website in IIS to release the lock, perform the build, then start the app pool again.
If you had configured Visual Studio to run using the local IIS website, then it would take care of stopping and starting the app pool for you, but JetBrains Rider doesn't do this.
The work around is to create two powershell scripts to stop and start the app pool, then configure pre and post build events when your web project build is triggered.
Here is the code for the two PowerShell scripts. Copy and paste the code into two files and store them in the root of the web project in Rider.
StopApplicationPool.ps1
$AppPoolName = "<app_pool_name>"
$AppPoolStatus = (Get-WebAppPoolState -Name $AppPoolName).Value
if ($AppPoolStatus -eq "Started") {
Write-Host "Stopping application pool $AppPoolName..."
Stop-WebAppPool -Name $AppPoolName
# Wait loop to check the status of the application pool
do {
Write-Host "Waiting for application pool to stop..."
Start-Sleep -Seconds 1 # Wait for 1 seconds before checking again
$AppPoolStatus = (Get-WebAppPoolState -Name $AppPoolName).Value
} while (($AppPoolStatus -eq "Started") -or ($AppPoolStatus -eq "Stopping"))
Write-Host "Application pool $AppPoolName has been stopped."
}
elseif ($AppPoolStatus -eq "Stopping") {
# Wait loop to check the status of the application pool
do {
Write-Host "Waiting for application pool to stop..."
Start-Sleep -Seconds 1 # Wait for 1 seconds before checking again
$AppPoolStatus = (Get-WebAppPoolState -Name $AppPoolName).Value
} while ($AppPoolStatus -eq "Stopping")
Write-Host "Application pool $AppPoolName has been stopped."
}
elseif ($AppPoolStatus -eq "Stopped") {
Write-Host "Application pool $AppPoolName is already stopped."
} else {
Write-Host "Application pool $AppPoolName is in an unknown state: $AppPoolStatus"
}
Notice the script above polls the app pool status until it's actually reporting the status as Stopped
, before exiting to allow the build to begin. Remember, application pools don't stop immediately, they shutdown gracefully to allow any processes that were in-progress, at the point when the stop
command was issued, to complete.
StartApplicationPool.ps1
$AppPoolName = "<app_pool_name>"
$AppPoolStatus = (Get-WebAppPoolState -Name $AppPoolName).Value
if ($AppPoolStatus -eq "Stopped") {
Write-Host "Starting application pool $AppPoolName..."
Start-WebAppPool -Name $AppPoolName
Write-Host "Application pool $AppPoolName has been started."
} elseif ($AppPoolStatus -eq "Started") {
Write-Host "Application pool $AppPoolName is already started."
} else {
Write-Host "Application pool $AppPoolName is in an unknown state: $AppPoolStatus"
}
Configure the pre and post build events
Pre build:
powershell -ExecutionPolicy Bypass -File "StopApplicationPool.ps1"
Post build:
powershell -ExecutionPolicy Bypass -File "StartApplicationPool.ps1"
In Rider, open the Properties for the project and copy and paste the above into the fields highlighted below.
That's it. Happy building 😀
Top comments (0)