Describe the bug
As part of #3984 FaultException was marked Serializable which was released in System.ServiceModel.Primitives 4.8.0
However I am still not able to use the use case as described by Wouter. Using code that serializes FaultExceptions using remoting. After updating my dependency on System.ServiceModel.Primitives I still receive the stack trace
Failed to deserialize and get remote exception System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.PlatformNotSupportedException: Operation is not supported on this platform.
at System.ServiceModel.CommunicationException..ctor(SerializationInfo info, StreamingContext context)
at System.ServiceModel.FaultException..ctor(SerializationInfo info, StreamingContext context)
at System.ServiceModel.FaultException`1..ctor(SerializationInfo info, StreamingContext context)
--- End of inner exception stack trace ---
at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
at System.Reflection.RuntimeConstructorInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)
at System.Runtime.Serialization.ObjectManager.FixupSpecialObject(ObjectHolder holder)
at System.Runtime.Serialization.ObjectManager.DoFixups()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(BinaryParser serParser, Boolean fCheck)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, Boolean check)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
at Microsoft.ServiceFabric.Services.Remoting.V2.RemoteException.TryDeserializeException(Stream data, Exception& result)
As far as I can tell that is because protected FaultException(SerializationInfo info, StreamingContext context) throws a PlatformNotSupportedException
In reference source that method is implemented https://github.com/microsoft/referencesource/blob/5697c29004a34d80acdaf5742d7e699022c64ecd/System.ServiceModel/System/ServiceModel/FaultException.cs#L120
The scenario is the same as described by Wouter in the referenced issue.
Is this something that also needs to be taken into account?
Thanks in advance,
Regards,
Max
@StephenBonikowsky @mconnew @HongGit since you were the main contributors on the original issue, can you share your insights if this should also be fixed?
My guess is that since we are serializing the FaultException the constructor should also be implemented
Thanks,
Max
Here's a complete repro that shows the difference between using:
@jnyrup Cool thanks for Sharing.
I also just created a quick example
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.ServiceModel;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
// #1 Plain FaultException using DataContractSerializer
var dcs = new DataContractSerializer(typeof(FaultException));
using (var ms = new MemoryStream())
using (var sr = new StreamReader(ms))
{
// Serialize + Print
dcs.WriteObject(ms, new FaultException("my fault reason"));
ms.Position = 0;
Console.WriteLine(sr.ReadToEnd());
// Deserialzie
// ms.Position = 0;
// var result = dcs.ReadObject(ms); // throws PlatformNotSupportedException: Operation is not supported on this platform.
}
Console.WriteLine("-------------------");
// #2 FaultException with FaultContract using DataContractSerializer
var dcs2 = new DataContractSerializer(typeof(FaultException<MyFaultContract>));
using (var ms = new MemoryStream())
using (var sr = new StreamReader(ms))
{
// Serialize + Print
dcs2.WriteObject(ms, new FaultException<MyFaultContract>(new MyFaultContract() { SomeValue = "1"}, new FaultReason("my reason")));
ms.Position = 0;
Console.WriteLine(sr.ReadToEnd());
// Deserialzie
// ms.Position = 0;
// var result = dcs2.ReadObject(ms); // throws PlatformNotSupportedException: Operation is not supported on this platform.
}
Console.WriteLine("-------------------");
// #3 Plain FaultException using BinaryFormatter
BinaryFormatter formatter = new BinaryFormatter();
using (var ms = new MemoryStream())
using (var sr = new StreamReader(ms)) {
// Serialize + Print
formatter.Serialize(ms, new FaultException("my fault reason"));
ms.Position = 0;
Console.WriteLine(sr.ReadToEnd());
// Deserialize
// ms.Position = 0;
// var result = formatter.Deserialize(ms); // throws PlatformNotSupportedException: Operation is not supported on this platform.
}
Console.WriteLine("-------------------");
// #4 FaultException with FaultContract using BinaryFormatter
BinaryFormatter formatter2 = new BinaryFormatter();
using (var ms = new MemoryStream())
using (var sr = new StreamReader(ms))
{
// Serialize + Print
formatter2.Serialize(ms, new FaultException<MyFaultContract>(new MyFaultContract() { SomeValue = "1" }, new FaultReason("my reason")));
ms.Position = 0;
Console.WriteLine(sr.ReadToEnd());
// Deserialize
// ms.Position = 0;
// var result = formatter2.Deserialize(ms); // throws PlatformNotSupportedException: Operation is not supported on this platform.
}
Console.WriteLine("-------------------");
Console.ReadLine();
}
}
[DataContract]
class MyFaultContract
{
[DataMember]
public string SomeValue { get; set; }
}
}
The above example demonstrates that both using BinaryFormatter and DataContractSerializer serialization appears to be working.
In all cases deserialization fails on the PlatformNotSupportedException.
Since our use case is to serialize the exception between services and then recreate them by deserializing to again have the Fault in the context of the service, we are unable to proceed with out use case at the moment
For completeness I also checked CommunicationException
public class CommunicationException : Exception
{
public CommunicationException() { }
public CommunicationException(string message) : base(message) { }
public CommunicationException(string message, Exception innerException) : base(message, innerException) { }
protected CommunicationException(SerializationInfo info, StreamingContext context) : base(info, context) { throw new PlatformNotSupportedException(); }
}
It also throws the PlatformNotSupportedException, I was not able to quickly find the dotnet repo containing the Exception class it self.
I can image this could be an issue.
Love to hear what you think about this issue :-)
@jnyrup Thanks, but that is interesting. That one is correctly serializable. Which would make it possible to implement the constructors in both FaultException and CommunicationException and stuff like binary serialization wil work again :-)
@mconnew can you please take a look at this one?
Most helpful comment
@jnyrup Cool thanks for Sharing.
I also just created a quick example
The above example demonstrates that both using BinaryFormatter and DataContractSerializer serialization appears to be working.
In all cases deserialization fails on the PlatformNotSupportedException.
Since our use case is to serialize the exception between services and then recreate them by deserializing to again have the Fault in the context of the service, we are unable to proceed with out use case at the moment