Skip to content

Implementing Nerdbank.MessagePack for serializing and deserializing messages #368

@AlleSchonWeg

Description

@AlleSchonWeg

Is your feature request related to a problem? Please describe.
Nerdbank.MessagePack library is the successor to MessagePack-CSharp. I created a custom marshaller to implement Nerdbank.MessagePack into ServiceModel.Grpc:

public sealed class NerdbankMessagePackMarshallerFactory : IMarshallerFactory
{
    private Nerdbank.MessagePack.MessagePackSerializer _serializer;
    public NerdbankMessagePackMarshallerFactory()
    {
        _serializer = new MessagePackSerializer { PerfOverSchemaStability = true };
    }

    public Marshaller<T> CreateMarshaller<T>()
    {
        return new Marshaller<T>(Serialize, Deserialize<T>);
    }

    private void Serialize<T>(T value, SerializationContext context)
    {

        var shape = ReflectionTypeShapeProvider.Default.GetShape<T>();
        _serializer.Serialize<T>(context.GetBufferWriter(), value, shape);
        context.Complete();
    }

    private T Deserialize<T>(DeserializationContext context)
    {
        var shape = ReflectionTypeShapeProvider.Default.GetShape<T>();
        return _serializer.Deserialize<T>(context.PayloadAsReadOnlySequence(), shape);
    }
}

Nerdbank.MessagePack uses PolyType as a source generator. The models must annotated with the GenerateShape attribute or by adding a witness type for a type that isn't declared in the project.
My problem is that ServiceModel.Grpc also uses a source generator for the messages. The type of the value parameter in the Serialize method are like this:

ServiceModel.Grpc.Channel.Message<System.DateTime>
ServiceModel.Grpc.Channel.Message<Contract.MyModel>
Client.MyGrpcClients.Message<string, int, int, string, string, int, bool, int, bool, int, int>

depending on the method signatures in the service contract interface:

[ServiceContract]
public interface IDemo
{
      [OperationContract]
      string TestDateTime(DateTime dt);
      [OperationContract]
      void TestObjectProperty(MyModel myModel);
      [OperationContract]
      void MultipleParameters(string p1, int p2, int p3, string p4, string p5, int p6, bool p7, int p8, bool p9, int p10, int p11);
}
Client:
//Client:
// configure ServiceModel.Grpc.DesignTime to generate a source code for IDemo client proxy
[ImportGrpcService(typeof(IDemo))]
internal static partial class MyGrpcClients
{}

In the NerdbankMessagePackMarshallerFactory above i use the ReflectionTypeShapeProvider.Default, because the models are wrapped as generic types in source generated ServiceModel.Grpc.Channel.Message<T1, T2, ..> or Client.MyGrpcClients.Message<T1, T2, ..>.

Is there a way make this work without reflection? Using a witness is also not possible.

[GenerateShapeFor<Client.MyGrpcClients.Message<string, int, int, string, string, int, bool, int, bool, int, int>>] //Compiler error
partial class Witness;

CC: @AArnott

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions