-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for MessagePack [Union] types as parameter and return types #490
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/azp run |
Azure Pipelines successfully started running 1 pipeline(s). |
Codecov Report
@@ Coverage Diff @@
## master #490 +/- ##
==========================================
+ Coverage 90.33% 90.36% +0.02%
==========================================
Files 50 50
Lines 3799 3891 +92
==========================================
+ Hits 3432 3516 +84
- Misses 367 375 +8
Continue to review full report at Codecov.
|
javierdlg
approved these changes
Jul 16, 2020
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
MessagePackFormatter
to use this type info to serialize using the declared type so that union data wraps the msgpack data when appropriate.Original problem
MessagePack requires knowing the declared type as well as the object to actually serialize/deserialize. The declared type might be
BaseType
while the value itself might be an instance ofDerivedType
. When the declared type equals the value's actual runtime type, MessagePack simply serializes the value. But when the declared type is a base type of the value's type, MessagePack looks for a[Union]
attribute on the declared type so it knows how to annotate the serialized value so that the runtime type can be recovered on the deserialized side.It is very important that the serializing and deserializing sides agree on the declared type so that they agree on what the type annotation (if any) should be, otherwise there is a deserialization failure.
The deserializing side always knows the declared type, as this is required to deserialize it in the first place. But while the serializing side always knows the runtime type (since it has the value and can call
GetType()
on it), the serializer does not always know the declared type. Thus, our MessagePack serialization code does not annotate values with the sometimes necessary Union type data, leading to a failure on the deserializing side.Design
We can divide the problem into two parts: parameters and return types.
Parameters are sent from the RPC client to the RPC server while return values go the opposite direction.
Because the problem is always on the serializing side, that means on the RPC server side we only need to worry about the declared type of the return value and on the RPC client side we only need to worry about the declared type of the arguments.
Return types
JsonRpc
can already discover the declared return types of RPC methods on the server side becauseJsonRpc
invoked the server method after all, so it has direct access toMethodInfo.ReturnType
.JsonRpc
needs to pass this declared return type to the formatter and will do this by way of a new property set onJsonRpcResult
. This property will not itself be serialized, and will only be set on the server.Parameter types
Parameter handling breaks down into two groups: positional and named.
Positional arguments
On the client side where arguments are serialized,
JsonRpc
only has anIReadOnlyList<object?>
of arguments. So we have runtime types, but no declared types, where the declared type for a given argument would be the parameter type on the RPC method on the server.JsonRpc.Invoke*
methods will need to take a newIReadOnlyList<Type>
parameter so the client can list the declared types of each argument as the server expects it.JsonRpc
needs to pass thisIReadOnlyList<Type>
to the formatter so it can use it while serializing arguments. It does this via a new property onJsonRpcRequest
. This property is not serialized, but the formatter will be able to use it on the client side and correlate each declared parameter type with the actual argument value.Dynamically generated proxies will lazily create a static array for each RPC method with the declared parameter types and pass it to
JsonRpc
.Named arguments
Named arguments are provided by a single object. Typically this object has properties to represent each parameter on the RPC method. In this case each argument is provided in its own property. When reflecting over this type and its properties to get the values, we have the
PropertyInfo
for each and can get the declared type from thePropertyInfo.PropertyType
property. We therefore don't need the client to provide any additional type information regarding these arguments.The
MessagePackFormatter
captures these property types just before serializing the single parameter object and uses them as necessary.In another scenario, the named arguments are passed as a name=value dictionary instead of a specialized type with a property for each named parameter.
In this case the
MessagePackFormatter
has no way to obtain the declared parameter types.JsonRpc
needs to collect this data from the caller and pass it along to the formatter.Unlike positional arguments that will use
IReadOnlyList<Type>
to represent each parameter type, named parameters will require anIReadOnlyDictionary<string, Type>
so each parameter's type can be looked up by name.It does this through a new property on
JsonRpcRequest
. This property is not serialized, but the formatter will be able to use it on the client side and correlate each declared parameter type with the actual argument value.Dynamically generated proxies support passing named arguments, but always with a specially generated parameter type, so they do not need to create a dictionary of property types, as the
MessagePackFormatter
will do it while reflecting over the special parameters type the proxy created.Closes #460