views:

785

answers:

4

I've inherited an ecommerce ASP.NET (c# code behind) web application. We've recently moved servers and it's proving somewhat troublesome. I have very little experience with IIS server configuration and dealing with large projects like this. Most of the problems have now been fixed, but we're experiencing problems with a crucial part, as a customer attempts to make a payment.
As the customer confirms payment, the application encounters the following error:

    Unable to serialize the session state. In 'StateServer' and 'SQLServer' mode, ASP.NET
will serialize the session state objects, and as a result non-serializable objects or 
MarshalByRef objects are not permitted. The same restriction applies if similar 
serialization is done by the custom session state store in 'Custom' mode.

Stack Trace:

[SerializationException: Type 'PayerAuthentication.PayerAuthenticationServicePost' in Assembly 'PayerAuthentication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.]
   System.Runtime.Serialization.FormatterServices.InternalGetSerializableMembers(RuntimeType type) +7733643
   System.Runtime.Serialization.FormatterServices.GetSerializableMembers(Type type, StreamingContext context) +258
   System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitMemberInfo() +111
   System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) +161
   System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.Serialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter) +51
   System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck) +410
   System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck) +134
   System.Web.Util.AltSerialization.WriteValueToStream(Object value, BinaryWriter writer) +1577

Google search results indicate I should add [Serializable] to the class declaration affected, but this is in a compiled dll to which I do not have the csproj. The code was working fine on the previous server and I do not believe any changes have been made to the code, only to web.config - what can I do?

The sessionstate section of web.config reads <sessionState mode="StateServer" />

UPDATE1: Using Reflector, I exported the class above, made it serializable, recompiled and replaced the dll. The order process went one step further, wherepon I encountered the same error for another dll-compiled class. Once again I was able to use Reflector to see the code, and then export it, edit and recompile.
Now I have the same error occurring in:

SerializationException: Type 'System.Runtime.Remoting.Messaging.AsyncResult' in Assembly 'mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' is not marked as serializable.]

I'm not sure I can do anything about this, as this must be part of the .net system files! Any further ideas?

UPDATE2: Ha, well I've subsequently discovered it's processing the payments correctly, but then throwing the above Unable to serialize the session state error on System.Runtime.Remoting.Messaging.AsyncResult before the user gets receipt of the transaction. Not good. Unsure how to move forward now...

UPDATE3: I tried creating a copy of the System.Runtime.Remoting.Messaging.AsyncResult class, and making it serializable but this is then leading to inconsistent accessibility problems.

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Security.Permissions;
using System.Runtime.Remoting.Messaging;

    [Serializable, ComVisible(true)]
    public class myAsyncResult : IAsyncResult, IMessageSink
    {
        // Fields
        private AsyncCallback _acbd;
        private Delegate _asyncDelegate;
        private object _asyncState;
        private ManualResetEvent _AsyncWaitHandle;
        private bool _endInvokeCalled;
        private bool _isCompleted;
        private IMessageCtrl _mc;
        private IMessage _replyMsg;

        // Methods
        internal myAsyncResult(Message m);
        //[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public virtual IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink);
        private void FaultInWaitHandle();
        public virtual IMessage GetReplyMessage();
        public virtual void SetMessageCtrl(IMessageCtrl mc);
        //[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
        public virtual IMessage SyncProcessMessage(IMessage msg);

        // Properties
        public virtual object AsyncDelegate { get; }
        public virtual object AsyncState { get; }
        public virtual WaitHandle AsyncWaitHandle { get; }
        public virtual bool CompletedSynchronously { get; }
        public bool EndInvokeCalled { get; set; }
        public virtual bool IsCompleted { get; }
        public IMessageSink NextSink { [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)] get; }
    }

Specifically, that error CS0122: 'System.Runtime.Remoting.Messaging.Message' is inaccessible due to its protection level. I can see this is because Message is an internal class. But surely I can't change the accessibility level of it as it is part of the System.Runtime namespace. And making a copy and renaming it is going to cuase the same problem again, surely?
Can anyone help me now?

+1  A: 

You'll need to find out if the new server is a later version than the old, or an older one. If it's an older version, then upgrade it to the newer version, and things should work.

If it's newer, then is it your code (that you have source to) that puts these non-serializable objects into session state? If so, then you can maybe create your own class to mirror the properties of the old class. Make your class serializable and put an instance of your class into session state. Make an instance of the old class when you take yours out of session state.

John Saunders
The server version is identical: 2.0.50727.3082
fearoffours
A: 

If the code was previously using just the in-memory state provider, then this could be... tricky. It is a pain point that the serialization process (via BinaryFormatter, which the database state provider uses) requires the [Serializable] attribute when the default provider doesn't.

How much of the code can you edit? Any of it? For example, can you change the code that puts things into/out-of state? You could perhaps use a separate (serializable) DTO with the necessary properties and translate between them with your own code.

Other options:

  • go back to the in-memory provider (and wave goodbye to a cluster)
  • write a provider that doesn't use BinaryFormatter

I have some thoughts on the latter, but I doubt it would be trivial

Marc Gravell
+1  A: 

If you really wanted the code, you could try using Reflector's Class View. At the very least, it could help you verify whether or not [Serializable] was part of the problem class definition or not.

Erich Mirabal
This has been brilliant. I've used Reflector to export the code, add `[Serializable]` to the class, compile it, overwrite the existing dll with the new one and it's solved the immediate problem, although another class is now triggering the same error. I'll be repeating the procedure for this class (and any suseuent ones) too.
fearoffours
Glad it helped. That was exactly what I thought you could do if the missing attribute was the problem.
Erich Mirabal
Yup, defienitely solved the immediate problem (twice, actually) but am now stuck as per updates in original post)
fearoffours
A: 

If the question is just how to make the application run without this error, the fast solution is to set the mode attribute of the sessionState element to "InProc".

Brian Reiter
No, I need the application to work correctly, not just run without the error. Changing the seesionState to "InProc" has the result of not updating the shopping cart when products are added to it!
fearoffours