Search code examples
c#.netserializationprotocol-buffersprotobuf-net

Protobuf-net create .proto with many classes & interfaces


Using: protobuf-net
I'm trying to use the:

string proto = Serializer.GetProto<YourType>();

But something the output I get is wrong.
I want to pass an interface so the client can have the methods to use & 2 class.

[ProtoContract]
public class Whatever
{
    [ProtoMember(1)]
    public string? Text { get; set; }
}

[ProtoContract]
public class Numb
{
    [ProtoMember(1)]
    public int? Num { get; set; }
}

[Service("Duplex")]
public interface IDuplex
{
    public IAsyncEnumerable<Whatever> Duplex(Numb num, CallContext context = default);
}

So I'm doing something along the line of:

using var writer = new StreamWriter("proto.proto");
writer.WriteLine(Serializer.GetProto<IDuplex>());
writer.WriteLine(Serializer.GetProto<Numb>());
writer.WriteLine(Serializer.GetProto<Whatever>());
writer.Close();

But I get:

syntax = "proto3";
package ConsoleApp1;

message IDuplex {
}

syntax = "proto3";
package ConsoleApp1;

message Numb {
   int32 Num = 1;
}

syntax = "proto3";
package ConsoleApp1;

message Whatever {
   string Text = 1;
}

Instead of something like:

syntax = "proto3";
package ConsoleApp1;

Service IDuplex {
   rpc Duplex(Numb) returns(Whatever);
}

message Numb {
   int32 Num = 1;
}

message Whatever {
   string Text = 1;
}

How can I chain many classes so I won't have that many:

syntax = "proto3";
package ConsoleApp1;

And create service to write into the same file?


Solution

  • protobuf-net by itself doesn't know about services; for that you need protobuf-net.Grpc, and in this case: protobuf-net.Grpc.Reflection, which has the SchemaGenerator type; usage:

    var gen = new SchemaGenerator();
    var schema = gen.GetSchema(typeof(IDuplex));
    Console.WriteLine(schema);
    

    This gives:

    syntax = "proto3";
    
    message Numb {
       int32 Num = 1;
    }
    message Whatever {
       string Text = 1;
    }
    service Duplex {
       rpc Duplex (Numb) returns (stream Whatever);
    }
    

    (the main reason that protobuf-net.Grpc.Reflection is a separate package here is that protobuf-net.Grpc only demands protobuf-net v2.x, but this feature requires v3.x APIs - hence protobuf-net.Grpc.Reflection forces a protobuf-net v3 transitive)