We are using an ASP.NET web site project with dynamic compilation, and recently moved to a SQL Server backed session state and started getting a strange error. I’ve figured out what is causing this, but I don’t know the best way to resolve it.
Steps to reproduce on localhost (with sql session enabled):
-
Place an object defined in AppCode, lets say it’s a
DanObjectinto the session.Session[“x”] = new DanObject();
-
(Session is serialized to the database)
- Modify something in app code, causing the site to recompile on the next request
- Make a request to any page that accesses the session
The error is “Unable to find assembly ‘App_SubCode_CS.rmdbqb81, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null’.”
What’s happening is that every time AppCode is compiled, it goes into a randomly named assembly. When my Session is serialized the first time, AppCode happened to be named AppCode_123. When I modified my application, AppCode is now AppCode_456. However the Session stored in my database has an object defined in AppCode_123. When the Session tries to binary deserialize the DanObject, it blows up because it can’t find AppCode_123.
What is the simplest way I can fix this?
*Please don’t say switch to Web Application–our code base is huge and it is not feasible at this point 🙂
The problem was we had separate folders in
App_Codefor different code languages. This is why in the error message, it said “App_SubCode_CS…” instead of “App_Code…”, because the specific class came from a CS code folder (for c# code). Each of these code folders (defined in web.config) get compiled into its own assembly.Normally when you don’t have multiple code folders AppCode, ASP.NET is able to serialize and deserialize objects compiled at different times or on different servers because the assembly name (“AppCode.randomstring”) is stored with the class name in the serialized output. On deserialization, the framework calls
System.Type.GetType()on the assembly + class name, and that function has a special case to handle assembly names starting withApp_Code, where if it doesn’t find an assembly named exactly the same, but it does find one that starts with App_Code, it uses that assembly to load the class.When you have language folders in
App_Code, the generated assemblies are named like:App_SubCode_FOLDERNAME.randomstring, and the framework doesn’t seem to handle this case. So if you have 2 webservers sharing a Sql backed session, class Foo is compiled as"App_SubCode_FN.random1, Foo"on server A, and"App_SubCode_FN.random2, Foo"on server B. If a user gets a Foo in his session from server A, and then his next request goes to server B, server B will be unable to deserialize the Foo because it can’t find an assembly named"App_SubCode_FN.random1".The problem can be solved by getting rid of your old VB code (allowing app code to be compiled into a single assembly that .NET plays more nicely with), or by writing a custom SerializationBinder + a custom implementation of the Sql Server backed session.