my job as a PFE for Microsoft, I read,
review and fix a lot of code. A lot of code. It’s a large part of what
I love about my job. The code is generally written by large corporations or for
public websites. Every now and again I’ll get pinged on an issue and after
troubleshooting the issue, it’s pretty clear that the core issue is with some community
code. When I say community code, in this instance, I don’t mean a CodeProject
or CodePlex project. In this case, I am referring to a control that Denis
Bauer created and then made available to the community on his website – the “DynamicControlsPlaceholder”
control. This is a great little control that inherits from a PlaceHolder
and allows you to create dynamic controls on the fly and then it will persist
the controls you add on subsequent requests – like a postback.
The customer was experiencing a problem that could only be replicated in a web farm
when they don’t turn on sticky
sessions. They found that when a request went from one server to another
server in their farm they would get a FileNotFoundException with the following details:
So, we can gleam a few things from the error details:
- They are using the ASP.NET website model (the “app_web_….dll” assembly is the clue).
The error is occurring in the RestoreChildStructure method of the DynamicControlsPlaceholder
The way that ASP.NET Websites work is that each component of your site can be compiled
into a separate assembly. The assembly name is randomly generated. This
also means that on two servers, the name of the assemblies can end up being different.
So, an assumption to make is that something is trying to load an assembly by its name.
If we look at the RestoreChildStructure method, we see the following:
The important thing to look at here is the Type.GetType(…) call. Since the code
for the control is in a separate assembly from everything else, the “typeName”
value MUST BE A FULLY QUALIFIED ASSEMBLY NAME. From the exception
details, we can see that it is attempting to load the type from the following string:
App_Web_myusercontrol.ascx.cc671b29.ypmqvhaw, Version=0.0.0.0, Culture=neutral,
The “typeName” variable is loaded from ViewState because that’s where the control
persists its child structure. So, for some reason the fully qualified assembly
name is stored in ViewState. If we look at the code that inserts the value into
ViewState (in the PersistChildStructure(…) method), we see:
So, here we see the AssemblyQualifiedName is being stored into ViewState – which is
then used to persist the controls across postback using the above code. As I
mentioned, this won’t work with an ASP.NET website hosted in a web farm because the
assembly qualified name will probably be different from server to server. We
even have a KB article that discusses this issue somewhat.
Fortunately, the fix is pretty simple.
First, we need to store the path to the User Control instead of the AQN in ViewState.
To do this, you can comment out the “typeName = ….” line from directly above and replace
So, now we store the path to the UserControl in ViewState. Then, we need to
fix the code that actually loads the control. Replace the code from above in
the RestoreChildStructure(…) method with this code:
That’s all there is to it. Just load the user control from where it is being
stored in the site and ASP.NET will take care of loading the appropriate assembly.