Search code examples
protobuf-netprotobuf-net.grpc

protobuf-net.Grpc serializing Interfaces


I am facing some difficulty serializing an interface using CodeFirst in Protobuf-net.grpc. It is a DataMember within a DataContract. It works fine when I use it in a simple console application but when I try to use it in a different project with the same references, it doesn't serialize. I can see this error in the Visual studio output as well, when the AddCodeFirst is done on the gRPC Server :

Exception thrown: 'System.InvalidOperationException' in protobuf-net.dll

This is the code I have for the DataContract in both my project and the dummy application

using ProtoBuf;

namespace ProtoBufTesting.Prepare
{
    [DataContract, ProtoContract]
    public class ExtensionData
    {
        public ExtensionData() { }

        public ExtensionData(
            string extensionName,
            string packageIdentifier,
IExtensionCertificateCollection collection)
        {
            this.ServiceName = extensionName;
            this.PackageIdentifier = packageIdentifier;
            this.CertificateCollection = collection;
        }

        [DataMember]
        [ProtoMember(1)]
        public string ServiceName
        {
            get; private set;
        }

        [DataMember]
        [ProtoMember(4)]

        public string PackageIdentifier
        {
            get; private set;
        }

        [DataMember]
        [ProtoMember(5)]

        public IExtensionCertificateCollection CertificateCollection { get; private set; }
    }
}

This is the interface IExtensionCertificateCollection that has the ProtoContract decorator which works in my sample project.

namespace ProtoBufTesting
{
    [ProtoContract]
    public interface IExtensionCertificateCollection
    {

        IExtensionCertificate GetByThumbprint(string thumbprint);

        IEnumerable<IExtensionCertificate> AllCertificates { get; }
    }
}

This is the service contract that uses the ExtensionData DataContract

[ServiceContract(Namespace = "blah")]
public interface IRTC
{

    [OperationContract]
    ExtensionData PrepareExtension(PrepareExtensionInput input);

}

This is how I do the Code First setup if you see below. This works perfectly fine other than in the second project where I'm doing the same thing. I am really not sure how to go about debugging this. The reason I did a sample console application is to test if Interfaces serialize using ProtoContract decorators and they did successfully with just the ProtoContract decorator... But when I started using it in a larger project (same interface definition and data contract), it just doesn't seem to serialize the DataContract. It only serializes the DataContract when I remove that interface from the the Datamember. Any ideas how I should go about debugging this to find out what is causing this? I've been stuck on this for a few hours. Other than the InvalidOperationException, I don't see any other errors in the VS output.

Server server = new Server
            {
                Ports = { new ServerPort("localhost", ServerPort.PickUnused , ServerCredentials.Insecure) }
            };

            server.Services.AddCodeFirst(new RTC());
            server.Start();
            int port = server.Ports.Single().BoundPort;

            Console.WriteLine("server listening on port " + port);

            GrpcClientFactory.AllowUnencryptedHttp2 = true;

            Channel channel = new Channel("localhost", port, ChannelCredentials.Insecure);
            

            var rtc = channel.CreateGrpcService<IRTC>();

Update:

I figured out a different way to 'debug' this - using this method below. It parsed the schema of the object I'm trying to serialize. Putting a try catch with this specific object gave me an error atleast! "Tags must be positive integers". I found this issue and fixed it.

ProtoBuf.Meta.RuntimeTypeModel.Default.GetSchema(typeof(ExtensionData));

But after that error, I still keep getting the previous still and that doesn't seem to resolve. I'm running this under a Unit test method and it only shows a high level error like this during the AddCodeFirst - I added a TraceWriter in the AddCodeFirst and this is what it says -

[warning] Type cannot be serialized; ignoring: ExtensionData
[warning] Signature not recognized for PrepareExtension; method will not be bound

How do I go about investigating this further? This is linked to the previous error I still get:

Exception thrown: 'System.InvalidOperationException' in protobuf-net.dll


Solution

  • I was finally able to solve this. Lots of types associated to the interface (and others) that were not getting deserialized correctly. I didn't see this in any error log though which made it hard to find.. searching online /looking through codebehind for protobuf-grpc.. it looked like adding explicit registrations of all the types that are required to be deserialized by the interface is required, else it won't work.

    I don't know if there is a better way to do this, but this was required just prior to the grPC AddCodeFirst else the service didn't ever register the methods without this being done first.

    ProtoBuf.Meta.RuntimeTypeModel.Default.Add(typeof(IExtensionCertificateCollection), true).AddSubType(1, typeof(ExtensionCertificateCollection)); 
    ProtoBuf.Meta.RuntimeTypeModel.Default.Add(typeof(IExtensionCertificateCollection), true).AddSubType(1, typeof(NewCollectionCerts));
    server.Services.AddCodeFirst(new FakeRTC(), log: writelogs);