dotnet-sdk
dotnet-sdk copied to clipboard
Actors uses different serializers for different purposes, not all of them are pluggable
Expected Behavior
Actors uses a single serializer by default, and allows flexiblity for different choices for compatibility or user freedom.
The problem with not providing a consistent default is that different serializers have non-overlapping features sets, and different configuration settings. The choice of serializer usually ends up being tightly coupled with the users' data model through attributes and other extensibility.
For instance DataContract serializer requires types and properties to "opt-in" to serialization - System.Text.Json assumes that types and public properties should be serialized - and you should "opt-out" for anything you want to exclude. It takes non-obvious work to migrate between these models, and many users are not familiar with the nuances.
Actual Behavior
Actors uses DataContract serializer (XML) for "remoting" method invocation (the choice of DCS is not configurable).
Actors uses System.Text.Json (JSON) for "non-remoting" method invocation (neither the choice of S.T.J nor the options passed to it are configurable).
Actors uses System.Text.Json (JSON) for state storage (the serializer and options have a replaceable abstraction).
Sidenote: Actors currently uses BinaryFormatter for exceptions in "remoting" method invocation, but that is planned for removal.
Steps to Reproduce the Problem
Build an Actor system that uses "remoting". Try to share data models between the state store and the actor method invocation. You'll find that you need to annotate data models for both DataContract serializer and System.Text.Json.
Release Note
We need to surface a design proposal and assess the impact of a change here. This is not a good users experience, but no simple change will fix it.
We're not going to make any changes here. This has some rough edges (using the same types with state and actor proxy invocation) but users that are using Actors today are overall happy with the experience. We'll revisit this in the future based on feedback.
We are actually having issues with this. Would be good to standardize on one serializer.
This is a also an issue for us. @rynowak I started conversation on discord in the dotnet-sdk channel. I want to make a proposal for the change. Can you please comment on that and let me know if you think I should create new proposal.
Having a ton of issues with this using records and STJ's polymorphism.
Can anyone explain what the OP means about 'non-remoting' actor invocations?
Is there an update to this issue?
Now we encounter that if some newer types (such as dateonly) are returned in the interface method of the actor, DCS(DataContract serializer)related errors will appear
Under Net7, STJ can support dateonly these types Now you can only temporarily use DateOnly to be converted to DateTime as an Actor's interface to external calls
Is there a documentation on unsupported actor method parameters serialization features? How can we make unit tests for our actors that validates the method parameter serialization?
Using Data Contract serialization is utterly excruciating in 2023. I would love to be able to transmit JsonElements without hacky workarounds.
What advantage does DCS have?
| Method | Mean | Error | StdDev | Gen0 | Allocated |
|---|---|---|---|---|---|
| DataContractSerialization | 1,102.4 ns | 1.39 ns | 1.30 ns | 0.3719 | 3120 B |
| SystemTextJsonSerialization | 297.7 ns | 0.31 ns | 0.28 ns | 0.0658 | 552 B |
We're also having this issue.
We expect a base class as an actor method parameter and want to store that in the actor state.
Currently on the base type we have to set "KnownTypes" for the client -> actor communication to get it serialized correctly and then we need to also need to add "JsonDerivedType" to get actor -> state communication working.
Is that really needed?
Any updates on this issue?
It is causing my team quite a bit of grief currently, making it very hard to use Dapr Actors for complex data types (Polymorphism).
@RobertConanMcMillan you can try using the experimental JSON serializer, unfortunately it's undocumented
on server:
on client:
Does the UseJsonSerialization property apply to actor state storage as well as actor invocation?
@onionhammer Thanks for the tip, unfortunately this doesn't seem to work correctly, do I need to provide some special JsonSerializerOptions?
All methods with parameters fail with this exception:
Dapr.Actors.ActorMethodInvocationException: Remote Actor Method Exception, DETAILS: Exception: JsonException, Method Name: Read, Line Number: 0, Exception uuid: 6e524ebf-2f91-4af7-9816-a17b9b427cad
---> Dapr.Actors.ActorInvokeException: The JSON value could not be converted to WrappedMessageBody. Path: $ | LineNumber: 0 | BytePositionInLine: 16.
--- End of inner exception stack trace ---
at Dapr.Actors.DaprHttpInteractor.InvokeActorMethodWithRemotingAsync(ActorMessageSerializersManager serializersManager, IActorRequestMessage remotingRequestRequestMessage, CancellationToken cancellationToken)
at Dapr.Actors.Communication.Client.ActorRemotingClient.InvokeAsync(IActorRequestMessage remotingRequestMessage, CancellationToken cancellationToken)
at Dapr.Actors.Client.ActorProxy.InvokeMethodAsync(Int32 interfaceId, Int32 methodId, String methodName, IActorRequestMessageBody requestMsgBodyValue, CancellationToken cancellationToken)
@darraghjones ~~It does, storing complex polymorphic models works perfectly with it enabled 👍~~
Edit: I was mistaken, it does not, by coincidence the model I was using could be serialized fine, but deserialization does not work.