1

我正在创建一个 gRPC 服务,我们决定使用 protobuf-net 选择代码优先方法。现在我遇到了一个场景,我们有几个需要包装的类。我们不想在 MyMessage 类中定义 KnownTypes(只是一个示例名称来说明问题)。所以我正在尝试使用目前让我在打包方面遇到一些困难的 Any 类型。

示例代码具有 MyMessage ,它定义了一些标头值,并且必须可以将任何类型作为有效负载传递。

[ProtoContract]
public class MyMessage 
{
  [ProtoMember(1)] public int HeaderValue1 { get; set; }
  [ProtoMember(2)] public string HeaderValue2 { get; set; }
  [ProtoMember(3)] public Google.Protobuf.WellknownTypes.Any Payload { get; set; }
}

[ProtoContract]
public class Payload1 
{
  [ProtoMember(1)] public bool Data1   { get; set; }
  [ProtoMember(2)] public string Data2 { get; set; }
}

[ProtoContract]
public class Payload2 
{
  [ProtoMember(1)] public string Data1 { get; set; }
  [ProtoMember(2)] public string Data2 { get; set; }  
}

在代码中的某处,我用有效负载构造了我的消息......

  Payload2 payload = new Payload2 {
    Data1 = "abc",
    Data2 = "def"
  };
  
  MyMessage msg = new MyMessage 
  {
    HeaderValue1 = 123,
    HeaderValue2 = "iAmHeaderValue2",
    Payload = Google.Protobuf.WellknownTypes.Any.Pack(payload)
  };

这不起作用,因为Payload1并且Payload2需要实施Google.Protobuf.IMessage. 由于我无法弄清楚如何并且根本找不到很多信息如何做到这一点,我想知道我是否走错了路。

  • 如何在 protobuf-net 中使用 Any?
  • 是否有一种简单(但兼容)的方法将 C# 代码第一类打包到 Google.Protobuf.WellknownTypes.Any 中?
  • 我真的需要实现 Google.Protobuf.IMessage 吗?
4

1 回答 1

0

首先,既然您说“我们有几个需要包装的类”(强调我的),我想知道您在这里真正想要的是oneof而不是Any. protobuf-net 支持该oneof概念,尽管从代码优先的角度来看并不明显。但是想象一下我们有(在合同优先的意义上):

syntax = "proto3";
message SomeType {
    oneof Content {
       Foo foo = 1;
       Bar bar = 2;
       Blap blap = 3;
    }
}

message Foo {}
message Bar {}
message Blap {}

这将(通过 protobuf-net 模式工具)实现为:

private global::ProtoBuf.DiscriminatedUnionObject __pbn__Content;

[global::ProtoBuf.ProtoMember(1, Name = @"foo")]
public Foo Foo
{
    get => __pbn__Content.Is(1) ? ((Foo)__pbn__Content.Object) : default;
    set => __pbn__Content = new global::ProtoBuf.DiscriminatedUnionObject(1, value);
}
public bool ShouldSerializeFoo() => __pbn__Content.Is(1);
public void ResetFoo() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__Content, 1);

[global::ProtoBuf.ProtoMember(2, Name = @"bar")]
public Bar Bar
{
    get => __pbn__Content.Is(2) ? ((Bar)__pbn__Content.Object) : default;
    set => __pbn__Content = new global::ProtoBuf.DiscriminatedUnionObject(2, value);
}
public bool ShouldSerializeBar() => __pbn__Content.Is(2);
public void ResetBar() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__Content, 2);

[global::ProtoBuf.ProtoMember(3, Name = @"blap")]
public Blap Blap
{
    get => __pbn__Content.Is(3) ? ((Blap)__pbn__Content.Object) : default;
    set => __pbn__Content = new global::ProtoBuf.DiscriminatedUnionObject(3, value);
}
public bool ShouldSerializeBlap() => __pbn__Content.Is(3);
public void ResetBlap() => global::ProtoBuf.DiscriminatedUnionObject.Reset(ref __pbn__Content, 3);

可选用枚举来帮助:

public ContentOneofCase ContentCase => (ContentOneofCase)__pbn__Content.Discriminator;

public enum ContentOneofCase
{
    None = 0,
    Foo = 1,
    Bar = 2,
    Blap = 3,
}

这种方法可能更容易和更可取Any


Any

简短版本:迄今为止,protobuf-net 还没有任何特定的要求来实现Any. 这可能不是大量的工作——简单地说:它还没有发生。看起来您在这里同时引用protobuf-netGoogle 库,并使用了Any. 这很好,但 protobuf-net 根本不会使用它——它不知道在这种情况下的 Google API,所以:实现IMessage实际上不会帮助你。

Any从 protobuf-net 方面来看,我非常乐意与您一起看。最终,时间/可用性始终是限制因素,因此我优先考虑看到需求的功能。我想你实际上可能是第一个Any在 protobuf-net 中问我的人。

于 2021-12-07T13:41:26.887 回答