runtime: Binary Serializing an object with a property of type 'Type' fails with SerializationException

(vs2017 15.3 preview 7.1. dotnet --version: 2.0.1-servicing-006924)

This code:

using System;
using System.Data.SqlClient;
using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace ConsoleTester
{
    class Program
    {
        static void Main(string[] args)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            MemoryStream stream = new MemoryStream();
            var toSerialize = new Foo() {Bar = "Bar", T = typeof(string)};
            formatter.Serialize(stream, toSerialize);
            stream.Seek(0, SeekOrigin.Begin);
            Foo deserialized = (Foo)formatter.Deserialize(stream);
            stream.Close();

            Console.WriteLine("Deserialized: " + deserialized.Bar);
        }
    }


    [Serializable]
    public class Foo
    {
        public string Bar { get; set; }
        public Type T { get; set; }
    }
}

results in:

Unhandled Exception: System.Runtime.Serialization.SerializationException: Type 'System.RuntimeType' in Assembly 'System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e' is not marked as serializable.
   at System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder)
   at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
   at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, BinaryFormatterWriter serWriter, Boolean fCheck)
   at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Boolean check)
   at ConsoleTester.Program.Main(String[] args) in C:\Myprojects\VS.NET Projects\LLBLGen Pro v5.3\UnitTests\LLBLGen Pro\LowLevelAPI\NUnitTests\SelfServicing\ConsoleTester\ConsoleTester\Program.cs:line 16
Press any key to continue . . .

Looking at RuntimeType in CoreCli, it indeed isn’t Serializable, while the type RuntimeType in .NET full is serializable.

This causes problems when porting code over from .NET full to .NET core 2.0 and using BinarySerialization. As the BinaryFormatter is available, one would assume it would work with normal arbitrary code like the simple class above.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Comments: 32 (29 by maintainers)

Commits related to this issue

Most upvoted comments

In case someone is in the same situation and finish looking at this issue. As a guide, I end up using a similar approach to Microsoft.Bot.Builder, using Surrogates to serialize System.Type and MemberInfo for BinaryFormatter.

System.Type (or anything else reflection-related) is unlikely to become serializable for a couple of reasons:

  1. It doesn’t translate well across runtimes, platforms, and versions. A given type may or may not exist, be in the same assembly, or have the same members.
  2. While .NET Core doesn’t promise that binary serialization is more secure than it is with .NET Framework, reflection types are part of many serialization attack gadgets. Those gadgets should hopefully be harder to find on .NET Core without them.

I don’t see it on the original lists of commonly serialized types that we based our list off

The dangers of static analysis.

but seems to throw?

Its GetObjectData uses info.SetType to essentially tell the deserializer to use a different type to deserialize it: https://referencesource.microsoft.com/#mscorlib/system/dbnull.cs,32 That’s done so that it’s deserialized as the DBNull singleton rather than as a unique instance.

@ViktorHofer The error came as a bit of a surprise, when we ported our .NET full tests over to .NET core 2.0 and a test broke on this issue. It appears that we serialize the type a field has into the data so we can rebuild the graph as-is on the other side as otherwise it’s not possible to obtain that data. I know the use-case isn’t that common, but it is what it is 😉

The workaround you suggest is indeed the one we came up with as well, which works OK. I just posted it here as it could have been an oversight. I’m sure I’m not alone who will run into this, but I also realize BinarySerializer isn’t a commonly used type.

System.RuntimeType isn’t serializable by design in .NET Core >=2.0. As I don’t have any usage data for it I kindly ask @morganbr to give some intel on that.

A workaround for your scenario would be to change serializing the FullName of the Type, which is a string, instead of the type object itself. During deserialization you can create the full Type via Type.GetType(string).