[英]specified method is not supported error on calling grpc service using protobuf-net library
My team uses Google grpc communication for micro service communication.我的团队使用 Google grpc 通信进行微服务通信。 I came across protobuf-net that is fast, reduces code complexity and no.proto file to be defined.
我遇到了 protobuf-net,它速度快,降低了代码复杂性,并且没有要定义的 proto 文件。 I wanted to give a try using protobuf-net to see if we gain considerable performance improvement.
我想尝试使用 protobuf-net 看看我们是否获得了相当大的性能提升。 However, I am getting error "specified method is not supported".
但是,我收到错误“不支持指定的方法”。 I think I am not able to mark the entity correctly.
我认为我无法正确标记实体。 I can use @marc-gravel help to understand the problem.
我可以使用@marc-gravel 帮助来理解问题。 Here are the details of my dotnet code
这是我的 dotnet 代码的详细信息
[ProtoContract]
public class ProtoBufInput
{
[ProtoMember(1)]
public string Id { get; set; }
[ProtoMember(2)]
public Building BuildingObj { get; set; }
[ProtoMember(3)]
public byte[] Payload { get; set; }
public ProtoBufInput(string id, Building buildingObj, byte[] payload)
{
BuildingObj = buildingObj;
Id = id;
Payload = payload;
}
}
[ProtoContract]
public class ProtoBufResult
{
[ProtoMember(1)]
public int RandomNumber { get; set; }
[ProtoMember(2)]
public bool RandomBool { get; set; }
[ProtoMember(3)]
public IList<string> ErrorMessages { get; set; }
[ProtoMember(5)]
public Building BuildingObj { get; set; }
[ProtoMember(6)]
public string RandomString { get; set; }
public ProtoBufResult()
{
RandomNumber = 0;
RandomBool = false;
}
}
[ProtoContract]
public class Building : Component<BuildingMetadata>
{
[ProtoMember(1)]
public string Id { get; set; }
[ProtoMember(2)]
public string tag { get; set; }
}
[ProtoContract]
public class BuildingMetadata : ComponentMetadata
{
[ProtoMember(1)]
public BuildingType Type { get; set; }
[ProtoMember(2)]
public bool IsAttached { get; set; }
public override object Clone()
{
var baseClone = base.Clone() as ComponentMetadata;
return new BuildingMetadata()
{
Model = baseClone.Model,
PropertyMetadata = baseClone.PropertyMetadata,
};
}
}
[ProtoContract]
public enum BuildingType
{
}
[ProtoContract]
public class ComponentMetadata : ICloneable
{
[ProtoMember(1)]
public string Model { get; set; }
[ProtoMember(2)]
public IDictionary<string, PropertyMetadata> PropertyMetadata { get; set; } = new Dictionary<string, PropertyMetadata>();
public virtual object Clone()
{
return new ComponentMetadata()
{
Model = Model,
PropertyMetadata = PropertyMetadata.ToDictionary(kvp => kvp.Key, kvp => kvp.Value.Clone() as PropertyMetadata),
};
}
}
[ProtoContract]
public class PropertyMetadata : ICloneable
{
[ProtoMember(1)]
[JsonProperty("Value")]
public JToken Value { get; set; }
[ProtoMember(2)]
[JsonProperty("Version")]
public int Version { get; set; }
[ProtoMember(3)]
[JsonProperty("backVersion")]
public int BackVersion { get; set; }
[ProtoMember(4)]
[JsonProperty("backCode")]
public int BackCode { get; set; }
[ProtoMember(5)]
[JsonProperty("Description")]
public string Description { get; set; }
[ProtoMember(6)]
[JsonProperty("createTime")]
public string CreateTime { get; set; }
public object Clone()
{
return new PropertyMetadata()
{
CreateTime = CreateTime ?? DateTime.UtcNow.ToString("o"),
};
}
}
[ProtoContract]
[ProtoInclude(1, typeof(Component<ComponentMetadata>))]
public class Component<TMetadataType> : ComponentBase, IComponent where TMetadataType : ComponentMetadata, new()
{
[ProtoMember(1)]
public TMetadataType Metadata { get; set; } = new TMetadataType();
public string Model => Metadata.Model;
public IEnumerable<(string, IComponent)> ListComponents() => Components.Select(x => (x.Key, x.Value as IComponent));
public IEnumerable<(string, JToken)> ListProperties() => Properties.Select(x => (x.Key, x.Value));
public ComponentMetadata GetMetadata() => Metadata;
public bool TryGetComponent(string name, out IComponent component)
{
component = null;
if (!Components.TryGetValue(name, out var innerComponent))
{
return false;
}
component = innerComponent as IComponent;
return true;
}
public bool TryGetProperty(string name, out JToken property) => Properties.TryGetValue(name, out property);
}
[ProtoContract]
public class ComponentBase
{
[ProtoMember(1)]
public IDictionary<string, JToken> Properties { get; set; } = new Dictionary<string, JToken>();
[ProtoMember(2)]
public IDictionary<string, InnerComponent> Components { get; set; } = new Dictionary<string, InnerComponent>();
}
[ProtoContract]
public class InnerComponent : Component<ComponentMetadata>
{
[ProtoMember(1)]
[JsonIgnore]
public string tag { get; set; }
}
Now coming to the service class and its implementation, I have something like this现在来到服务 class 及其实现,我有这样的东西
[ServiceContract]
public interface IProtoBufService
{
[OperationContract]
public Task<ProtoBufResult> ProcessPb(ProtoBufInput input, CallContext context = default);
}
public class ProtoBufService : IProtoBufService
{
public Task<ProtoBufResult> ProcessPb(ProtoBufInput protoBufInput, CallContext context)
{
...
}
}
Rest of the configuration in start up file is correct like adding启动文件中配置的 Rest 是正确的,如添加
serviceCollection.AddCodeFirstGrpc();
builder.MapGrpcService<Services.V2.ProtoBufService>();
You have three problems with your serialization code:您的序列化代码存在三个问题:
As noted by Marc Gravell , Protobuf-net does not know how to serialize Json.NET's JToken
objects.正如Marc Gravell所指出的,Protobuf-net 不知道如何序列化 Json.NET 的
JToken
对象。
Since JToken
objects are intended to represent free-form JSON, the easiest way to serialize them with Protobuf-net is to serialize surrogate string
properties instead that represent the raw JSON value:由于
JToken
对象旨在表示自由形式的 JSON,因此使用 Protobuf-net 序列化它们的最简单方法是序列化代理string
属性,而不是表示原始 JSON 值:
[ProtoContract] public class PropertyMetadata: ICloneable { [ProtoMember(1)] string SerializedValue { get => Value?.ToString(Formatting.None); set => Value = (value == null? null: JToken.Parse(value)); } // FIXED
and和
public class ComponentBase { [ProtoMember(1)] string SerializedProperties { get => Properties == null? null: JsonConvert.SerializeObject(Properties); set => Properties = (value == null? null: JsonConvert.DeserializeObject<Dictionary<string, JToken>>(value)); }
Note I am serializing the entire IDictionary<string, JToken> Properties
object as a single JSON object.注意我将整个
IDictionary<string, JToken> Properties
object 序列化为单个 JSON object。
When serializing an inheritance hierarchy, Protobuf-net requires that every base class TBase
be informed of the existence of all immediate derived classes TDerived
.在序列化 inheritance 层次结构时,Protobuf-net 要求每个基本 class
TBase
被告知所有直接派生类TDerived
的存在。 This can be done via attributes by adding这可以通过添加属性来完成
[ProtoContract] [ProtoInclude(N, typeof(TDerived))] public class TBase { }
to the base class.到底座 class。 Note that the numbers
N
must be unique and not overlap with any ProtoMemberAttribute.Tag
values so it is wise to start them from a large number such as 1000:请注意,数字
N
必须是唯一的,并且不能与任何ProtoMemberAttribute.Tag
值重叠,因此明智的做法是从大数字(例如 1000)开始:
[ProtoContract] [ProtoInclude(1001, typeof(BuildingMetadata))] public class ComponentMetadata: ICloneable
[ProtoContract] [ProtoInclude(1002, typeof(Building))] [ProtoInclude(1001, typeof(InnerComponent))] public class Component<TMetadataType>: ComponentBase, IComponent where TMetadataType: ComponentMetadata, new()
[ProtoContract] [ProtoInclude(1002, typeof(Component<BuildingMetadata>))] [ProtoInclude(1001, typeof(Component<ComponentMetadata>))] public class ComponentBase
In your demo fiddle , your class Component<TMetadataType>
has a get-only property Model
which you are serializing:在您的演示小提琴中,您的 class
Component<TMetadataType>
有一个 get-only 属性Model
您正在序列化:
[ProtoMember(2)] public string Model => Metadata.Model;
With the other two problems fixed, for some reason this property causes the serializer to throw the following exception:修复其他两个问题后,由于某种原因,此属性会导致序列化程序抛出以下异常:
System.InvalidOperationException: Unable to wrap ComponentBase/ComponentBase: Unable to bind serializer: It was not possible to prepare a serializer for: ComponentBase (ProtoBuf.Internal.Serializers.InheritanceTypeSerializer`2[ComponentBase,ComponentBase])
This can be resolved by either removing Model
from serialization, or adding a private dummy setter like so:这可以通过从序列化中删除
Model
或添加一个私有虚拟设置器来解决,如下所示:
[ProtoMember(2)] public string Model { get => Metadata.Model; private set { } } // Private set required for serialization
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.