psi icon indicating copy to clipboard operation
psi copied to clipboard

Serialization Exception when opening templated objects from another framework.

Open xiangzhi opened this issue 5 years ago • 1 comments

This might be related to #74. I have a PsiStore recorded on Windows/.NET Framework and wanted to reply it on a Linux/.NET Core. Currently it only have one stream of type: List<MathNet.Spatial.CoordinateSystem>.

When I try to reopen the stream on the Linux machine, the following inner exception is thrown:

An unhandled exception of type 'System.AggregateException' occurred in Microsoft.Psi.dll: 'Pipeline 'default' was terminated because of one or more unexpected errors'
 Inner exceptions found, see $exception in variables window for more details.
 Innermost exception 	 System.Runtime.Serialization.SerializationException : Failed to create a deserializer for type urn:MathNet/Numerics/LinearAlgebra:DenseColumnMajorMatrixStorageOfdouble because no type was registered for this name and the source type MathNet.Numerics.LinearAlgebra.Storage.DenseColumnMajorMatrixStorage`1[[System.Double, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], MathNet.Numerics, Version=4.9.1.0, Culture=neutral, PublicKeyToken=cd8b63ad3d691a37 could not be found. Add a reference to the assembly containing this type, or register an alternate type for this name.
   at Microsoft.Psi.Serialization.KnownSerializers.GetUntypedHandler(Int32 handlerId, Type baseType) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/KnownSerializers.cs:line 392
   at Microsoft.Psi.Serialization.RefHandler`1.Deserialize(BufferReader reader, T& target, SerializationContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/RefHandler.cs:line 120
   at Microsoft.Psi.Serialization.ClassSerializer`1.Deserialize(BufferReader reader, T& target, SerializationContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/ClassSerializer.cs:line 82
   at Microsoft.Psi.Serialization.RefHandler`1.InnerDeserialize(BufferReader reader, T& target, SerializationContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/RefHandler.cs:line 267
   at Microsoft.Psi.Serialization.RefHandler`1.Deserialize(BufferReader reader, T& target, SerializationContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/RefHandler.cs:line 139
   at Microsoft.Psi.Serialization.ArraySerializer`1.Deserialize(BufferReader reader, T[]& target, SerializationContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/ArraySerializer.cs:line 48
   at Microsoft.Psi.Serialization.RefHandler`1.InnerDeserialize(BufferReader reader, T& target, SerializationContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/RefHandler.cs:line 267
   at Microsoft.Psi.Serialization.RefHandler`1.Deserialize(BufferReader reader, T& target, SerializationContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/RefHandler.cs:line 139
   at Microsoft.Psi.Serialization.ClassSerializer`1.Deserialize(BufferReader reader, T& target, SerializationContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/ClassSerializer.cs:line 82
   at Microsoft.Psi.Serialization.RefHandler`1.InnerDeserialize(BufferReader reader, T& target, SerializationContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/RefHandler.cs:line 267
   at Microsoft.Psi.Serialization.RefHandler`1.Deserialize(BufferReader reader, T& target, SerializationContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Serialization/RefHandler.cs:line 139
   at Microsoft.Psi.Components.DeserializerComponent`1.Receive(Message`1 msg, Envelope envelope) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Components/DeserializerComponent.cs:line 43
   at Microsoft.Psi.Pipeline.<>c__DisplayClass104_0`1.<CreateReceiver>b__0(Message`1 m) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Executive/Pipeline.cs:line 367
   at Microsoft.Psi.Executive.PipelineElement.<>c__DisplayClass45_1`1.<TrackStateObjectOnContext>b__1() in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Executive/PipelineElement.cs:line 184
   at Microsoft.Psi.Executive.PipelineElement.<>c__DisplayClass44_0.<TrackStateObjectOnContext>b__0() in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Executive/PipelineElement.cs:line 162
   at Microsoft.Psi.Executive.PipelineElement.<>c__DisplayClass45_0`1.<TrackStateObjectOnContext>b__0(T m) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Executive/PipelineElement.cs:line 184
   at Microsoft.Psi.Receiver`1.<>c__DisplayClass13_0.<.ctor>b__0(Message`1 m) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Streams/Receiver{T}.cs:line 52
   at Microsoft.Psi.Receiver`1.DeliverNext() in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Streams/Receiver{T}.cs:line 297
   at Microsoft.Psi.Scheduling.Scheduler.ExecuteAndRelease(SynchronizationLock synchronizationObject, Action action, SchedulerContext context) in /home/xiangzht/Dev/Psi/psi/Sources/Runtime/Microsoft.Psi/Scheduling/Scheduler.cs:line 542

I did some digging into the problem and it's because the typefinder (/Sources/Runtime/Microsoft.Psi/Common/TypeResolutionHelper.cs) cannot find the templated type System.Double, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089. When the typefinder is looking for the assembly, it is missing in .NET Core.

This is because .NET Framework and .NET Core use different base assemblies to store these basis data types. .NET Framework uses mscorlib whereas .NET Core uses System.Private.CoreLib.

I was able to fix this with this really hacky fix in TypeResolutionHelper.cs:

        private static Assembly AssemblyResolver(AssemblyName assemblyName)
        {
            // Get the list of currently loaded assemblies
            Assembly[] loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();

           // Fix the name difference between .NET Core and .NET Framework
            if (assemblyName.FullName.StartsWith("mscorlib"))
            {
                return loadedAssemblies.FirstOrDefault(a => a.GetName().FullName.StartsWith("System.Private.CoreLib"));
            }

            // Attempt to match by full name first
            var assembly = loadedAssemblies.FirstOrDefault(a => a.GetName().FullName == assemblyName.FullName);
            if (assembly != null)
            {
                return assembly;
            }

            // Otherwise try to match by simple name without version, culture or key
            assembly = loadedAssemblies.FirstOrDefault(a => AssemblyName.ReferenceMatchesDefinition(a.GetName(), assemblyName));
            if (assembly != null)
            {
                return assembly;
            }

            return null;
        }

I'm pretty sure there are more elegant and better fix, but just want y'all know of this issue. Here's the store I used for testing: list-of-coordinate-frames.zip

xiangzhi avatar Aug 14 '20 19:08 xiangzhi

Thanks. We actually do have a fix for this which we will push out in a release within a couple of weeks. Hopefully this workaround that you've come up with will tide you over until then?

chitsaw avatar Aug 15 '20 01:08 chitsaw