[英]How to use (pack) Google.Protobuf.WellknownTypes.Any in protobuf-net code first gRPC
I am creating an gRPC service and we decided to choose the code first approach with protobuf-net.我正在创建一个 gRPC 服务,我们决定使用 protobuf-net 选择代码优先方法。 Now I am running into a scenario where we have a couple of classes that need to be wrapped.
现在我遇到了一个场景,我们有几个需要包装的类。 We do not want to define KnownTypes in the MyMessage class (just a sample name to illustrate the problem).
我们不想在 MyMessage class 中定义 KnownTypes(只是一个示例名称来说明问题)。 So I am trying to use the Any type which currently gives me some struggle with packing.
所以我正在尝试使用目前让我在打包方面遇到一些困难的 Any 类型。
The sample code has the MyMessage which defines some header values and has to possiblity to deliver any type as payload.示例代码具有 MyMessage,它定义了一些 header 值,并且必须可以将任何类型作为有效负载传递。
[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; }
}
Somewhere in the code I construct my message with a payload...在代码中的某处,我用有效负载构造了我的消息......
Payload2 payload = new Payload2 {
Data1 = "abc",
Data2 = "def"
};
MyMessage msg = new MyMessage
{
HeaderValue1 = 123,
HeaderValue2 = "iAmHeaderValue2",
Payload = Google.Protobuf.WellknownTypes.Any.Pack(payload)
};
Which doesn't work because Payload1
and Payload2
need to implement Google.Protobuf.IMessage
.这不起作用,因为
Payload1
和Payload2
需要实现Google.Protobuf.IMessage
。 Since I can't figure out how and do not find a lot information how to do it at all I am wondering if I am going a wrong path.由于我无法弄清楚如何并且根本找不到很多信息如何做到这一点,我想知道我是否走错了路。
Firstly, since you say "where we have a couple of classes that need to be wrapped" (emphasis mine), I wonder if what you actually want here is oneof
rather than Any
.首先,既然您说“我们有几个需要包装的类”(强调我的),我想知道您在这里真正想要的是
oneof
而不是Any
。 protobuf-net has support for the oneof
concept, although it isn't obvious from a code-first perspective. protobuf-net 支持
oneof
概念,尽管从代码优先的角度来看并不明显。 But imagine we had (in a contract-first sense):但是想象一下我们有(在合同优先的意义上):
syntax = "proto3";
message SomeType {
oneof Content {
Foo foo = 1;
Bar bar = 2;
Blap blap = 3;
}
}
message Foo {}
message Bar {}
message Blap {}
This would be implemented (via the protobuf-net schema tools) as:这将(通过 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);
optionally with an enum to help:可选用枚举来帮助:
public ContentOneofCase ContentCase => (ContentOneofCase)__pbn__Content.Discriminator;
public enum ContentOneofCase
{
None = 0,
Foo = 1,
Bar = 2,
Blap = 3,
}
This approach may be easier and preferable to Any
.这种方法可能比
Any
更容易且更可取。
On Any
:在
Any
:
Short version: protobuf-net has not, to date, had any particular request to implement Any
.简短版本:迄今为止,protobuf-net 还没有任何特定的要求来实现
Any
。 It probably isn't a huge amount of work - simply: it hasn't yet happened .这可能不是大量的工作——简单地说:它还没有发生。 It looks like you're referencing both protobuf-net and the Google libs here, and using the Google implementation of
Any
.看起来您在这里同时引用了protobuf-net和Google 库,并使用了 Google 的
Any
实现。 That's fine, but protobuf-net isn't going to use it at all - it doesn't know about the Google APIs in this context, so: implementing IMessage
won't actually help you.这很好,但是 protobuf-net 根本不会使用它——它不知道在这种情况下的 Google API,所以:实现
IMessage
实际上不会帮助你。
I'd be more than happy to look at Any
with you, from the protobuf-net side.我很乐意从 protobuf-net 方面与您一起研究
Any
。 Ultimately, time/availability is always the limiting factor, so I prioritise features that are seeing demand.最终,时间/可用性始终是限制因素,因此我优先考虑看到需求的功能。 I think you may actually be the first person asking me about
Any
in protobuf-net.我想你实际上可能是第一个问我关于 protobuf-net 中的
Any
的人。
I wrote this hacky Any implementation.我写了这个 hacky Any 实现。
[ProtoContract(Name = "type.googleapis.com/google.protobuf.Any")]
public class Any
{
/// <summary>Pack <paramref name="value"/></summary>
public static Any Pack(object? value)
{
// Handle null
if (value == null) return new Any { TypeUrl = null!, Value = Array.Empty<byte>() };
// Get type
System.Type type = value.GetType();
// Write here
MemoryStream ms = new MemoryStream();
// Serialize
RuntimeTypeModel.Default.Serialize(ms, value);
// Create any
Any any = new Any
{
TypeUrl = $"{type.Assembly.GetName().Name}/{type.FullName}",
Value = ms.ToArray()
};
// Return
return any;
}
/// <summary>Unpack any record</summary>
public object? Unpack()
{
// Handle null
if (TypeUrl == null || Value == null || Value.Length == 0) return null;
// Find '/'
int slashIx = TypeUrl.IndexOf('/');
// Convert to C# type name
string typename = slashIx >= 0 ? $"{TypeUrl.Substring(slashIx + 1)}, {TypeUrl.Substring(0, slashIx)}" : TypeUrl;
// Get type (Note security issue here!)
System.Type type = System.Type.GetType(typename, true)!;
// Deserialize
object value = RuntimeTypeModel.Default.Deserialize(type, Value.AsMemory());
// Return
return value;
}
/// <summary>Test type</summary>
public bool Is(System.Type type) => $"{type.Assembly.GetName().Name}/{type.FullName}" == TypeUrl;
/// <summary>Type url (using C# type names)</summary>
[ProtoMember(1)]
public string TypeUrl = null!;
/// <summary>Data serialization</summary>
[ProtoMember(2)]
public byte[] Value = null!;
/// <summary></summary>
public static implicit operator Container(Any value) => new Container(value.Unpack()! );
/// <summary></summary>
public static implicit operator Any(Container value) => Any.Pack(value.Value);
/// <summary></summary>
public struct Container
{
/// <summary></summary>
public object? Value;
/// <summary></summary>
public Container()
{
this.Value = null;
}
/// <summary></summary>
public Container(object? value)
{
this.Value = value;
}
}
}
'System.Object' can be used as a field or property of another record with following gimmick: 'System.Object' 可以用作另一个记录的字段或属性,具有以下噱头:
[DataContract]
public class Dummy
{
/// <summary></summary>
[DataMember(Order = 1, Name = nameof(Value))]
public Any.Container Any { get => new Any.Container(Value); set => Value = value.Value; }
/// <summary>Object</summary>
public object? Value;
}
Serialization序列化
RuntimeTypeModel.Default.Add(typeof(Any.Container), false).SetSurrogate(typeof(Any));
var ms = new MemoryStream();
RuntimeTypeModel.Default.Serialize(ms, new Dummy { Value = "Hello world" });
Dummy dummy = RuntimeTypeModel.Default.Deserialize(typeof(Dummy), ms.ToArray().AsMemory()) as Dummy;
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.