dotnet / orleans

Cloud Native application framework for .NET
https://docs.microsoft.com/dotnet/orleans
MIT License
10.07k stars 2.03k forks source link

[Question] Exception passthrough from grain to client fails due to deserialization error. #3153

Closed kumbhare closed 7 years ago

kumbhare commented 7 years ago

We have custom exception thrown by the grains. Custom exception are marked as [Serializable], and are present in a common DLL that is referenced in both grains and the client.

Seems like the exceptions are serialized correctly, but deserialization fails with following error:

InnerException": InnerException": {
    "Message": "An error has occurred.",
    "ExceptionMessage": "The constructor to deserialize an object of type 'Treehouse.Server.Common.Exceptions.ObjectNotFoundException' was not found.",
    "ExceptionType": "System.Runtime.Serialization.SerializationException",
    "StackTrace": "   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n   at Treehouse.Website.ApiControllers.UserApiController.<>c__DisplayClass0_0.<<GetUserById>b__0>d.MoveNext() in D:\\Treehouse\\src\\src\\Treehouse.Website\\Server\\WebApi\\UserApiController.cs:line 29\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n   at Treehouse.Website.ApiControllers.UserApiController.<>c__DisplayClass5_0`1.<<ExecuteWithExceptionHandling>b__0>d.MoveNext() in D:\\Treehouse\\src\\src\\Treehouse.Website\\Server\\WebApi\\UserApiController.cs:line 104\r\n--- End of stack trace from previous location where exception was thrown ---\r\n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()\r\n   at Treehouse.Website.ApiControllers.BaseApiController.<ExecuteWithExceptionHandling>d__4`1.MoveNext() in D:\\Treehouse\\src\\src\\Treehouse.Website\\Server\\WebApi\\BaseApiController.cs:line 65",
    "InnerException": {
      "Message": "An error has occurred.",
      "ExceptionMessage": "The constructor to deserialize an object of type 'Treehouse.Server.Common.Exceptions.ObjectNotFoundException' was not found.",
      "ExceptionType": "System.Runtime.Serialization.SerializationException",
      "StackTrace": "   at System.Runtime.Serialization.ObjectManager.GetConstructor(RuntimeType t)\r\n   at System.Runtime.Serialization.ObjectManager.CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)"
    }
  }

I also tried adding KnownType, KnownAssembly but that does not help either. Is there a way to pass through exceptions from grains to clients?

gabikliot commented 7 years ago

I think it has to do with properly implementing the custom exception. Exceptions are a special weird beast in .NET. It is not enough to mark is as [Serializable]. You need to properly implement 4 constructors. See example here: https://github.com/dotnet/orleans/blob/ac6f1d331197935c22d3394659498ed32cf2b6dd/src/Orleans/Streams/QueueAdapters/DataNotAvailableException.cs

This is completely unrelated to Orleans. Orleans does not generate any serializers for Exceptions and fully relies on .NET binary serialization for exceptions.

@ReubenBond , if this is indeed the case here, I would suggest we catch the failure to deserialize exception in our serializer and if it is indeed for an Exception, we add more details to the error msg explaining how to implement a custom exception. That way next time people would be able to figure this out.

gabikliot commented 7 years ago

You can see from the exc msg that it is failing to find this third constructor: CompleteISerializableObject(Object obj, SerializationInfo info, StreamingContext context)"

https://stackoverflow.com/questions/94488/what-is-the-correct-way-to-make-a-custom-net-exception-serializable

kumbhare commented 7 years ago

Thanks Gabi.. will try that out..

ReubenBond commented 7 years ago

Hi @kumbhare, did @gabikliot's suggestion work for you?

kumbhare commented 7 years ago

Hi @ReubenBond, yup @gabikliot's suggestion works for us for our custom exceptions.

I have a question in general though. I tested with .Net exceptions, and the serialization/deserialization works fine for these, but what would happen with third party exceptions? I was expecting RemoteNonDeserializableException per #2310, #2633. I am on 1.4.2

sergeybykov commented 7 years ago

There was also #2999 later.

ReubenBond commented 7 years ago

What are you seeing instead of the fallback exception, @kumbhare? In order to take advantage of the fallback exceptions, you must configure ILBasedSerializer as your fallback serializer - it will not be used on Full .NET Framework by default otherwise

kumbhare commented 7 years ago

Thanks @sergeybykov @ReubenBond. I will try with ILBasedSerializer and update the thread. But I am unblocked on my original question, so we can close this.