Thursday, May 01, 2008

Beware of non-specific references

I recently completed a change to a web application that utilizes the ASP.NET AJAX web extensions (System.Web.Extensions.dll). This assembly is loaded into the Global Assembly Cache (GAC) and referenced to there by the web project. I ran the web app locally without any issues. After updating the source code repository with my changes, I asked the build server to create a new release candidate of the app. This worked fine.

I then deployed it to the staging/test server and hit the URL. Failure! The error I received was

"Parser Error Message: The base class includes the field 'UpdatePanel1', but its type (System.Web.UI.UpdatePanel) is not compatible with the type of control (System.Web.UI.UpdatePanel)."

Clearly, a System.Web.UI.UpdatePanel is a System.Web.UI.UpdatePanel. So I investigated further. My web.config file contained this:

<compilation defaultLanguage="c#" debug="true">
<add assembly="System.Web.Extensions, Version=1.0.61025.0,
Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

This provides the assembly version the web app is loading for the creation of the dynamically compiled pages. Thus, the update panel created from the markup is the one from the version 1.0.61025.0 assembly.

I started looking at the web application assembly. Using BeyondCompare with a conversion rule to process .DLLs with ILDASM I was able to look at the compiled assembly references. I found my version of the assembly to have the reference as

.assembly extern System.Web.Extensions
.publickeytoken = (31 BF 38 56 AD 36 4E 35 ) // 1.8V.6N5
.ver 1:0:61025:0

while the build server's version had

.assembly extern System.Web.Extensions
.publickeytoken = (31 BF 38 56 AD 36 4E 35 ) // 1.8V.6N5
.ver 3:5:0:0

So, the code behind instance of the update panel in the prebuilt web app assembly is from the referenced assembly while the runtime instance from the markup is the other version. This was the culprit.

This happened because Visual Studio created the reference to the assembly that is in the GAC but created it with the "Specific Version" flag set to "false". When the project was built on the build server it took the newer assembly for the reference. I changed the flag to "true" in the project.

After committing the change I asked for another build. Now the build server built assembly had the correct version referenced and the app runs.

1 comment:

Anonymous said...

Awesome find. Just what I needed.