Zaid-Ajaj / Fable.Remoting

Type-safe communication layer (RPC-style) for F# featuring Fable and .NET Apps
https://zaid-ajaj.github.io/Fable.Remoting/
MIT License
272 stars 54 forks source link

Error in BinarySerialization - Cannot serialize Exception #259

Closed tforkmann closed 3 years ago

tforkmann commented 3 years ago

Error msg:

fail: Shared.IDataServerApi[0]
      error occured
      System.Exception: Cannot serialize Exception.
         at Microsoft.FSharp.Core.PrintfModule.PrintFormatToStringThenFail@1433.Invoke(String message) in D:\workspace\_work\1\s\src\fsharp\FSharp.Core\printf.fs:line 1433
         at System.Runtime.CompilerServices.RuntimeHelpers.DispatchTailCalls(IntPtr callersRetAddrSlot, IntPtr callTarget, IntPtr retVal)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.serializerCached[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.makeMemberVisitor@332-1.TypeShape.IReadOnlyMemberVisitor<'T, System.Action<'T, System.IO.Stream>>.Visit[a](ReadOnlyMember`2 field)
         at Fable.Remoting.MsgPack.Write.mapping@1-3[T](TypeGenerationContext ctx, FSharpTypeFunc w, ShapeFSharpUnionCase`1 c)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux$cont@339[T](TypeGenerationContext ctx, FSharpTypeFunc w, TypeShape`1 matchValue, Unit unitVar)
         at System.Runtime.CompilerServices.RuntimeHelpers.DispatchTailCalls(IntPtr callersRetAddrSlot, IntPtr callTarget, IntPtr retVal)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.serializerCached[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.makeMemberVisitor@332-1.TypeShape.IReadOnlyMemberVisitor<'T, System.Action<'T, System.IO.Stream>>.Visit[a](ReadOnlyMember`2 field)
         at Fable.Remoting.MsgPack.Write.mapping@1-3[T](TypeGenerationContext ctx, FSharpTypeFunc w, ShapeFSharpUnionCase`1 c)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux$cont@339[T](TypeGenerationContext ctx, FSharpTypeFunc w, TypeShape`1 matchValue, Unit unitVar)
         at System.Runtime.CompilerServices.RuntimeHelpers.DispatchTailCalls(IntPtr callersRetAddrSlot, IntPtr callTarget, IntPtr retVal)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.serializerCached[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.makeMemberVisitor@332-1.TypeShape.IReadOnlyMemberVisitor<'T, System.Action<'T, System.IO.Stream>>.Visit[a](ReadOnlyMember`2 field)
         at Fable.Remoting.MsgPack.Write.mapping@1-3[T](TypeGenerationContext ctx, FSharpTypeFunc w, ShapeFSharpUnionCase`1 c)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux$cont@339[T](TypeGenerationContext ctx, FSharpTypeFunc w, TypeShape`1 matchValue, Unit unitVar)
         at System.Runtime.CompilerServices.RuntimeHelpers.DispatchTailCalls(IntPtr callersRetAddrSlot, IntPtr callTarget, IntPtr retVal)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.serializerCached[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.makeMemberVisitor@332-1.TypeShape.IReadOnlyMemberVisitor<'T, System.Action<'T, System.IO.Stream>>.Visit[a](ReadOnlyMember`2 field)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.serializerCached[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.makeMemberVisitor@332-1.TypeShape.IReadOnlyMemberVisitor<'T, System.Action<'T, System.IO.Stream>>.Visit[a](ReadOnlyMember`2 field)
         at Fable.Remoting.MsgPack.Write.mapping@1-3[T](TypeGenerationContext ctx, FSharpTypeFunc w, ShapeFSharpUnionCase`1 c)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux$cont@339[T](TypeGenerationContext ctx, FSharpTypeFunc w, TypeShape`1 matchValue, Unit unitVar)
         at System.Runtime.CompilerServices.RuntimeHelpers.DispatchTailCalls(IntPtr callersRetAddrSlot, IntPtr callTarget, IntPtr retVal)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.serializerCached[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.makeMemberVisitor@332-1.TypeShape.IReadOnlyMemberVisitor<'T, System.Action<'T, System.IO.Stream>>.Visit[a](ReadOnlyMember`2 field)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.serializerCached[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.makeSerializerAux@361-24.TypeShape.ITypeVisitor<System.Action<'T, System.IO.Stream>>.Visit[a]()
         at Fable.Remoting.MsgPack.Write.serializerCached[T](TypeGenerationContext ctx)
         at Fable.Remoting.MsgPack.Write.makeSerializer[T]()
         at Fable.Remoting.Server.Proxy.msgPackSerialize[a](a o, Stream stream)
         at Fable.Remoting.Server.Proxy.makeEndpointProxy@69-8.Invoke(result _arg1)
         at Microsoft.FSharp.Control.AsyncPrimitives.CallThenInvokeNoHijackCheck[a,b](AsyncActivation`1 ctxt, FSharpFunc`2 userCode, b result1) in D:\workspace\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 404
         at Microsoft.FSharp.Control.Trampoline.Execute(FSharpFunc`2 firstAction) in D:\workspace\_work\1\s\src\fsharp\FSharp.Core\async.fs:line 104
Error at /api/IDataServerApi/HeartBeatTable on method HeartBeatTable exn: Cannot serialize Exception.

Here response type:

type CurrentPiStatus =
    | RunningOk
    | IsStopped of ClientError
    | RequiresPi
    | RequiresUpdate
    | RequiresRestart
    | RequiresMapping
    | Updating of AttemptCount
    | Restarting of AttemptCount
    | Unresponsive of LastAttempt
    override this.ToString() =
        match this with
        | RunningOk -> "RunningOk"
        | IsStopped err -> "ClientError" + err.ToString()
        | RequiresUpdate -> "RequiresUpdate"
        | RequiresRestart -> "RequiresRestart"
        | RequiresPi -> "RequiresPi"
        | Updating _ -> "Updating"
        | Restarting _ -> "Restarting"
        | Unresponsive _ -> "Unresponsive"
        | RequiresMapping -> "RequiresMapping"

  type PiStaticIP =
      | PiStaticIP of piStaticIP: string
      member this.GetValue = (fun (PiStaticIP id) -> id) this

type LocationPlantCombo =
    { LocationId: Ids.LocationId
      PlantId: Ids.PlantId }
    member this.GetValue =
        this.LocationId.GetValueAsString
        + "-"
        + this.PlantId.GetValueAsString

type PiHeartBeat =
    { CurrentPiStatus: CurrentPiStatus
      HeartBeat: DateTimeOffset
      FirmwareVersion: string
      PiStaticIP: PiStaticIP
      PlantName: string
      LocationPlantCombo: LocationPlantCombo }

type PlantWithHeartBeat =
    { HeartBeat: PiHeartBeat option
      Plant: Plant }

Transfered Msg is a PlantWithHeartBeat array

The msg is quite big. msg2.txt

kerams commented 3 years ago

Well, Exception is not supported (does it work with JSON?). You might have to define an explicit DU for the error types or just use a string (for the message and/or call stack).

tforkmann commented 3 years ago

There is no error in the data. It is just a array. The exception only appears if I add in binarySerialization. It works very without binarySerialization.

kerams commented 3 years ago

MsgPack constructs a serializer for the entire model at first, while JSON (I believe) only works off the current message. What happens in JSON if you include CurrentPiStatus.IsStopped, where I assume the exception is nested? We might be able to implement it, but the exception type information would be lost, you'd just have the message, and I think that would possibly be worse than nothing. Inheritance is one of the reasons why classes in general aren't supported either.

Zaid-Ajaj commented 3 years ago

@tforkmann can you share the type of ClientError? If it has an Ezception type there, it won't work, even with JSON. The reasoning is that exceptions shouldn't be transferred over the network, especially when some of them might contain sensitive data

tforkmann commented 3 years ago

@Zaid-Ajaj I'm getting following error msg from the Client

response { StatusCode = 500
  ResponseBody = {"error":"Error occured while running the function HeartBeatTable","ignored":true,"handled":true} } responseText {"error":"Error occured while running the function HeartBeatTable","ignored":true,"handled":true} status code 500 exn Internal server error (500) while making request to /api/IDataServerApi/HeartBeatTable

I don't think that helps much.

Zaid-Ajaj commented 3 years ago

What happens in JSON if you include CurrentPiStatus.IsStopped, where I assume the exception is nested?

👆

@tforkmann can you share the type of ClientError?

👆

tforkmann commented 3 years ago
type ErrorCode =
      | ConnectionFailure = 3
      | ReceiveTimeout = 4
      | DataReceive = 5
      | TCPDataSend = 7
      | TCPNotConnected = 9

type ClientError =
    | ChannelError of S7Error.ErrorCode
    | DataError
    | NoDataFromCurrentMappingInfo
    | NoMappingInfo
    | NoClientStatusInDb of exn option

JSON still works even with if I set CurrentPiStatus.IsStopped (ChannelError (ErrorCode.ConnectionFailure ))

tforkmann commented 3 years ago

The issue is NoClientStatusInDb. The JSON is then failing as well.

tforkmann commented 3 years ago

Ok that was it!

Thanks. Serialization works well now!