Closures leading to “Type not marked as serializable” exception

Today I faced a SerializationException that refered to some anonymous inner class stating it was not serializable, when IIS tried to store the session in the ASP.NET State Service:

Type ‘Xyz+<>c__DisplayClass10’ in Assembly ‘Xyz, Version=1.2.5429.24450, Culture=neutral, PublicKeyToken=null’ is not marked as serializable.

I looked for lambdas in my code and found quite a few, but most of them were not new and did never have any issues in serialization. But then I noticed that I had built in a new lambda expression that “happened” to build up a closure.

I have built a very simple example to confirm that closures are not serializable whereas “normal” functions are:

[TestFixture]
public class GeneralUnderstandingTests
{
    [Serializable]
    private class ASerializableType
    {
        private readonly Func thisIsAClosure;
        private readonly Func thisIsNotAClosure;

        public ASerializableType()
        {
            // Normal functions are allowed: The following
            // succeeds to serialize
            const int SomeConst = 12345;
            thisIsNotAClosure = () => SomeConst;

            // But closures are compiled to non-serializable
            // inner classes and break serialization:
            var someVariable = 12345;
            thisIsAClosure = () => someVariable;
        }
    }

    [Test]
    public void ASerializableType_CanBeSerialized()
    {
        var sessionState = SessionStateItemCollection();
        sessionState["sut"] = new ASerializableType();
        sessionState.Serialize(
            new BinaryWriter(new MemoryStream()));
    }
}

This test fails but goes green as soon as the line thisIsAClosure = … is commented out. The line thisIsNotAClosure = … however does not cause any issues as SomeConst is not a variable but a constant, that is, it does not build a closure but is inlined by the compiler.

Two Useful Classes When Using Excel COM Interop

There are two known problems when working with Excel Interop.

  1. After having used Excel and shut down your application, an Excel process still “hangs” in background.
  2. When your Excel installation is English (en-US) only, but your Windows is configured for a different language (e.g. de-DE), some calls to the Interop fail with a COMException.

Luckily there are also workarounds for this problem which you can easily find when googling for them. The following classes have been handy for me to encapsulate these workarounds. Both providing IDisposable.

This way, you can use them as follows:

Excel.Workbooks myWorkbooks
        = myExcelApplication.Workbooks;
Excel.Workbook myWorkbook;

using (new Disposer(myWorkbooks))
    using (new EnUsCulture())
        myWorkbook = myWorkbooks.Open(fileName, [...]);

The Disposer takes care of properly disposing of the COM object. The EnUsCulture switches the current thread’s culture to en-US right on creation. On Dispose(), it switches the culture back to what it was before. Using “using” you make sure the thread’s culture is switched back even if an error occurs.

Disposer:

    public class Disposer : IDisposable
    {
        private object _comObject;

        public Disposer(object comObject)
        {
            _comObject = comObject;
        }

        public void Dispose()
        {
            if (_comObject != null)
            {
                Marshal.ReleaseComObject(_comObject);
                _comObject = null;
                GC.Collect();
            }
        }
    }

 
EnUsCulture:

    public class EnUsCulture : IDisposable
    {
        private CultureInfo _oldCulture;

        public EnUsCulture()
        {
            if (Thread.CurrentThread.CurrentCulture
                .Equals(new CultureInfo("en-US")))
            {
                _oldCulture = null;
            }
            else
            {
                _oldCulture = Thread.CurrentThread.CurrentCulture;
                Thread.CurrentThread.CurrentCulture
                    = new CultureInfo("en-US");
            }
        }

        public void Dispose()
        {
            if (_oldCulture != null)
                Thread.CurrentThread.CurrentCulture = _oldCulture;
        }
    }

Note that the Dispose() method only resets the thread’s culture if it wasn’t already en-US before. This is to prevent the following situation:

    EnUsCulture c1 = new EnUsCulture(); // switch from de-DE to en-US
    EnUsCulture c2 = new EnUsCulture(); // switch from en-US to en-US
    c1.Dispose(); // switch back to de-DE
    c2.Dispose(); // switch back to en-US

The thread would end up with the en-US culture if c2 is disposed after c1. To prevent that, we make c2 notice on its creation that the culture has already been switched before, so it won’t have any effect at all:

    EnUsCulture c1 = new EnUsCulture(); // switch from de-DE to en-US
    EnUsCulture c2 = new EnUsCulture(); // no effect
    c1.Dispose(); // switch back to de-DE
    c2.Dispose(); // no effect