【问题标题】:How to use (pack) Google.Protobuf.WellknownTypes.Any in protobuf-net code first gRPC如何在 protobuf-net 代码优先 gRPC 中使用(打包)Google.Protobuf.WellknownTypes.Any
【发布时间】:2022-01-12 12:31:03
【问题描述】:

我正在创建一个 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)
  };

这不起作用,因为Payload1Payload2 需要实现Google.Protobuf.IMessage。 由于我无法弄清楚如何做,也没有找到很多信息,所以我想知道我是否走错了路。

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

【问题讨论】:

  • 如果您只是将您的有效负载转换为Dictionary<string, string>,这可能是最简单的。 [ProtoInclude] 似乎用于继承。 IMessage 看起来像手动序列化(读/写字段的字节)。

标签: c# grpc .net-5 protobuf-net protobuf-net.grpc


【解决方案1】:

首先,既然您说“我们有一个几个类需要被包装”(强调我的),我想知道您实际上是什么 这里要的是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。这可能不是大量的工作 - 简单地说:它还没有发生。看起来您在这里引用了 both protobuf-net Google 库,并使用了 Any 的 Google 实现。没关系,但 protobuf-net 根本不会使用它——它不知道在这种情况下的 Google API,所以:实现 IMessage 实际上对你没有帮助。

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

【讨论】:

  • 很高兴收到您的来信。我们今天已经谈了很多关于你的事情。出于两个原因,我们决定选择 Any 而不是其中之一。首先 MyMessage 成为一个不知道实际有效负载的可再分发库。其次,使用该库的项目将有“一对 100 多条消息”:-) 在 oneof 中创建一个相当长的列表
  • @monty 好吧,Any 并不能完全拯救你;即使在 Google API 中,您也会面临“100 多个”版本的 TryUnpack<T>(...)Is(...) 调用;没有“给我任何东西,你想办法”的 API:github.com/protocolbuffers/protobuf/blob/master/csharp/src/…(尽管如果我们在 protobuf-net 中实现Any,我希望这是我们可以改进的东西)
  • :-) 有趣的是,我是第一个问任何问题的人。认为这将是常见的用例。因为我认为澄清会破坏 cmets 部分,所以我允许自己直接给你发邮件。
猜你喜欢
  • 2021-11-04
  • 2021-04-24
  • 2021-05-07
  • 1970-01-01
  • 2022-12-15
  • 2019-08-03
  • 2019-03-23
  • 2020-10-02
  • 1970-01-01
相关资源
最近更新 更多