Wednesday, December 3, 2008

Watch out with Assembly.LoadFile in ASP.NET

Today I ran into a very annoying problem in Visual Web Developer 2008 SP1. When I tried to compile my web solution, I got this warning and sometimes even error in the Error List:

Unable to update auto-refresh reference 'Assembly.dll'. Cannot copy assembly 'Assembly.dll' to file 'X:\PathToWebProjectDirectory\Bin\Assembly.dll'.  Unable to add 'X:\PathToExternalLibrary\Assembly.dll' to the Web site.  Unable to add file 'Bin\Assembly.dll'.  The process cannot access the file because it is being used by another process.

Of course, paths and file names will be different in your case. If you would search this problem on the internet, then you will find several forum topics about this issue and eventually some support articles: KB843370 and KB313512.

Although I followed these instructions, the problem was still not resolved. Then I looked into something mentioned in some of the forums postings; to check which application was holding the handles to the file, causing it not to be updated. With the Sysinternals Process Explorer I could identify that the ASP.NET Developement Server (Webdev.Webserver.EXE) was holding the handle.

By that time I realized that my own code loads that specific assembly to extract some resources. I used the Assembly.LoadFile method to load this assembly. This will cause the file-lock on the assembly making it impossible for Visual Studio to AutoUpdate the file, because the built-in ASP.NET Development Server is blocking the assembly.

So why does this not happen with other assemblies in the ASP.NET Development Server and only with my code? The reason for this can be found in the way how ASP.NET makes a cache of all the assemblies it loads. These copies are placed in a folder managed by ASP.NET. These files will be locked, but as they are copies, the locks cause no harm.

I found a solution in changing from the Assembly.LoadFile method to the Assembly.Load method. Note that you can call the Assembly.Load method multiple times, but it will physically load the assembly only once. Even though I pass only the assembly name as the string argument, ASP.NET finds its cached copy of the assembly and loads that .dll instead of the one in the Bin folder of my project. Now Visual Studio can overwrite the assembly in the Bin folder without a hassle, and I'm happy too. :-)