Confused by the GAC
Forgive me coder for I have sinned. I put some code into a dll in the GAC, and now I am confused about how the GAC is really working with IIS.
In our intranet environment I have several related web applications (about 10). Each application runs from a separate url, and potentially runs in its own app pool. For now all of the apps are on the same server, but that is not required. This setup allows us to control, develop and migrate each app separately. However, there are several parts of each app that we wanted to be common across all of our applications (master pages, logon pages, menus ect.) The request from management was that changes to these files could be universal, and would take effect without having to re-compile and re-migrate each web app. There are lots of ways to accomplish this task, and despite warnings to avoid the GAC, this really seemed like a case where using a Virtual path provider and a dll stored in the GAC made sense.
I put together a Virtual path provider, and then embedded the files and resources I needed to be common into a dll. (I could have had the VPP pull the files from a database, but I was hoping that embedding the files into the dll itself would be more performant.
Complication #1
I didn’t want the developers of each app to have to mess with their own GAC to test and run their application, so in each solution I just included the Virtual Path Provider project, and made its result dll, copy local, (as well as strongly named), this means that the provider dll, along with its embedded files are copied to the bin directory of the application. As long as the developer doesn’t have a version of this dll in their GAC (which they shouldn’t) the local bin copy is used. This allows the developer to easily change and test both the app’s files and the files that are common to every application.This also means that unless it is deliberately prevented, the common dll will exist in the bin directory of every app in production as well. However, since IIS will always use the GAC version if it exists, even if the local version is out of date, the GAC version is always used.
I accepted this as reasonable for now since I couldn’t find an easy way to both have the VPP.dll copy local for development, and yet not copy to the bin directory, when the app was copied to the server.
Complication #2
Actually coding an app so that it always uses the latest version of the dll instead of the specific version that it was compiled with seems like a fairly complicated matter. ( I have yet to see a good example of how to do it, but I think I would need a chain of publisher policies) It seemed much easier to try the more obvious answer – leave the version number of the dll unchanged when migrating a new version of the VPP dll to the GAC. This seemed to work ok, until it didn’t.
Using the Windows /assembly snap-in on the server, I replaced an older version of v. 1.0.0.0 of my dll in the GAC with an updated copy of v. 1.0.0.0 . Even though I had updated the dll in the GAC, my apps continued to use the old version of the code. Restarting IIS, and even rebooting the server entirely did not seem to help. What really flipped me out was that eventually IIS would mysteriously and unexpectedly correct itself and start using the correct copy of the dll from the GAC.
I have seen many GAC related questions of this type that all seemed to end with “all of the sudden my website started working correctly again”. It took a while for me to see that this was an effect of how IIS was caching my strongly named dll, and that I was mis-using the way strongly named dll’s were supposed to work.
Next I tried removing the version of the dll from the GAC entirely. Because there was still a copy of my dll in the bin directory, I expected that the process would either fail, or revert/advance to the version that was in the bin directory. However because the version in the bin was also 1.0.0.0, IIS continued to use the cached copy of the dll, which now no longer existed on the machine.(That had me really baffled for a while, because I lost confidence that the GAC version was ever running at all)
Finally the only way I had to assuredly get the dll to update was to remove the copy of the dll from the GAC, remove the copy of the dll from the bin directory, run the site so that it would fail, and then put the new version of the dll into the GAC. Starting and stopping IIS during this had no effect. This consistently got my applications to use the new code but…
So much for simply sticking a new version of a dll into the GAC and having its new effects propagate seamlessly.
After reading some related issues, What I am trying now is:
1. Stopping IIS
2. Updating thesoftware of the dll in the GAC (but still leaving the version number of the new dll at 1.0.0.0 )
3. deleting everything in the 'C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files' folder ( I think this forces a recompile, but it’s a sledgehammer of a fix)
4. restart IIS.
This appears to be working, but It still seems like there is something wrong with having to go to all this trouble. Is there an easier way to force IIS to use the current instance of the GAC’s dll? Is there a better way to accomplish the effect that I am after. (some common files .aspx and .xml files shared among various apps, that can be updated separately from the apps themselves.)
In our intranet environment I have several related web applications (about 10). Each application runs from a separate url, and potentially runs in its own app pool. For now all of the apps are on the same server, but that is not required. This setup allows us to control, develop and migrate each app separately. However, there are several parts of each app that we wanted to be common across all of our applications (master pages, logon pages, menus ect.) The request from management was that changes to these files could be universal, and would take effect without having to re-compile and re-migrate each web app. There are lots of ways to accomplish this task, and despite warnings to avoid the GAC, this really seemed like a case where using a Virtual path provider and a dll stored in the GAC made sense.
I put together a Virtual path provider, and then embedded the files and resources I needed to be common into a dll. (I could have had the VPP pull the files from a database, but I was hoping that embedding the files into the dll itself would be more performant.
Complication #1
I didn’t want the developers of each app to have to mess with their own GAC to test and run their application, so in each solution I just included the Virtual Path Provider project, and made its result dll, copy local, (as well as strongly named), this means that the provider dll, along with its embedded files are copied to the bin directory of the application. As long as the developer doesn’t have a version of this dll in their GAC (which they shouldn’t) the local bin copy is used. This allows the developer to easily change and test both the app’s files and the files that are common to every application.This also means that unless it is deliberately prevented, the common dll will exist in the bin directory of every app in production as well. However, since IIS will always use the GAC version if it exists, even if the local version is out of date, the GAC version is always used.
I accepted this as reasonable for now since I couldn’t find an easy way to both have the VPP.dll copy local for development, and yet not copy to the bin directory, when the app was copied to the server.
Complication #2
Actually coding an app so that it always uses the latest version of the dll instead of the specific version that it was compiled with seems like a fairly complicated matter. ( I have yet to see a good example of how to do it, but I think I would need a chain of publisher policies) It seemed much easier to try the more obvious answer – leave the version number of the dll unchanged when migrating a new version of the VPP dll to the GAC. This seemed to work ok, until it didn’t.
Using the Windows /assembly snap-in on the server, I replaced an older version of v. 1.0.0.0 of my dll in the GAC with an updated copy of v. 1.0.0.0 . Even though I had updated the dll in the GAC, my apps continued to use the old version of the code. Restarting IIS, and even rebooting the server entirely did not seem to help. What really flipped me out was that eventually IIS would mysteriously and unexpectedly correct itself and start using the correct copy of the dll from the GAC.
I have seen many GAC related questions of this type that all seemed to end with “all of the sudden my website started working correctly again”. It took a while for me to see that this was an effect of how IIS was caching my strongly named dll, and that I was mis-using the way strongly named dll’s were supposed to work.
Next I tried removing the version of the dll from the GAC entirely. Because there was still a copy of my dll in the bin directory, I expected that the process would either fail, or revert/advance to the version that was in the bin directory. However because the version in the bin was also 1.0.0.0, IIS continued to use the cached copy of the dll, which now no longer existed on the machine.(That had me really baffled for a while, because I lost confidence that the GAC version was ever running at all)
Finally the only way I had to assuredly get the dll to update was to remove the copy of the dll from the GAC, remove the copy of the dll from the bin directory, run the site so that it would fail, and then put the new version of the dll into the GAC. Starting and stopping IIS during this had no effect. This consistently got my applications to use the new code but…
So much for simply sticking a new version of a dll into the GAC and having its new effects propagate seamlessly.
After reading some related issues, What I am trying now is:
1. Stopping IIS
2. Updating thesoftware of the dll in the GAC (but still leaving the version number of the new dll at 1.0.0.0 )
3. deleting everything in the 'C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files' folder ( I think this forces a recompile, but it’s a sledgehammer of a fix)
4. restart IIS.
This appears to be working, but It still seems like there is something wrong with having to go to all this trouble. Is there an easier way to force IIS to use the current instance of the GAC’s dll? Is there a better way to accomplish the effect that I am after. (some common files .aspx and .xml files shared among various apps, that can be updated separately from the apps themselves.)
Labels: c#