问题描述
我有一个 WCF 服务,我想将其重写为 gRPC 服务。有一个特定的端点给我带来了一些麻烦。现在这个方法看起来像这样:
public List<Dictionary<string,string> GetData(GetDataRequest request)
{
List<Dictionary<string,string>> results = new List<Dictionary<string,string>>();
/// ...
/// Code that populates the results list
/// ...
return results;
}
我已经像这样编写了 proto 文件:
message GetDataRequest {
string code = 1;
}
message GetDataResponse {
message keyvaluePair {
map<string,string> pairs = 1;
}
repeated keyvaluePair results= 1;
}
service Demo {
rpc GetData(GetDataRequest) returns (GetDataResponse);
}
以及服务实现:
public class DemoService : Demo.DemoBase
{
public override async Task<GetDataResponse> GetData(GetDataRequest request,ServerCallContext context)
{
List<Dictionary<string,string>>();
/// ...
/// Code that populates the results list
/// ...
return await Task.Fromresult(new GetDataResponse
{
Results = results
});
}
}
我的问题是当我尝试返回字典列表时出现此错误:
我需要做哪些更改才能正确返回响应? 我使用 Visual Studio 2019 gRPC 服务模板。
这是 protobuf 编译器生成的 GetDataResponse 代码:
public sealed partial class GetDataResponse : pb::IMessage<GetDataResponse>
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE,pb::IBufferMessage
#endif
{
private static readonly pb::MessageParser<GetDataResponse> _parser = new pb::MessageParser<GetDataResponse>(() => new GetDataResponse());
private pb::UnkNownFieldSet _unkNownFields;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser<GetDataResponse> Parser { get { return _parser; } }
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pbr::MessageDescriptor Descriptor {
get { return global::GrpcService.Protos.DemoReflection.Descriptor.MessageTypes[1]; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public GetDataResponse() {
OnConstruction();
}
partial void OnConstruction();
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public GetDataResponse(GetDataResponse other) : this() {
results_ = other.results_.Clone();
_unkNownFields = pb::UnkNownFieldSet.Clone(other._unkNownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public GetDataResponse Clone() {
return new GetDataResponse(this);
}
/// <summary>Field number for the "results" field.</summary>
public const int ResultsFieldNumber = 1;
private static readonly pb::FieldCodec<global::GrpcService.Protos.GetDataResponse.Types.keyvaluePair> _repeated_results_codec
= pb::FieldCodec.ForMessage(10,global::GrpcService.Protos.GetDataResponse.Types.keyvaluePair.Parser);
private readonly pbc::RepeatedField<global::GrpcService.Protos.GetDataResponse.Types.keyvaluePair> results_ = new pbc::RepeatedField<global::GrpcService.Protos.GetDataResponse.Types.keyvaluePair>();
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public pbc::RepeatedField<global::GrpcService.Protos.GetDataResponse.Types.keyvaluePair> Results {
get { return results_; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override bool Equals(object other) {
return Equals(other as GetDataResponse);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool Equals(GetDataResponse other) {
if (ReferenceEquals(other,null)) {
return false;
}
if (ReferenceEquals(other,this)) {
return true;
}
if(!results_.Equals(other.results_)) return false;
return Equals(_unkNownFields,other._unkNownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override int GetHashCode() {
int hash = 1;
hash ^= results_.GetHashCode();
if (_unkNownFields != null) {
hash ^= _unkNownFields.GetHashCode();
}
return hash;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override string ToString() {
return pb::JsonFormatter.ToDiagnosticString(this);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void Writeto(pb::CodedOutputStream output) {
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
output.WriterawMessage(this);
#else
results_.Writeto(output,_repeated_results_codec);
if (_unkNownFields != null) {
_unkNownFields.Writeto(output);
}
#endif
}
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
void pb::IBufferMessage.InternalWriteto(ref pb::WriteContext output) {
results_.Writeto(ref output,_repeated_results_codec);
if (_unkNownFields != null) {
_unkNownFields.Writeto(ref output);
}
}
#endif
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public int CalculateSize() {
int size = 0;
size += results_.CalculateSize(_repeated_results_codec);
if (_unkNownFields != null) {
size += _unkNownFields.CalculateSize();
}
return size;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom(GetDataResponse other) {
if (other == null) {
return;
}
results_.Add(other.results_);
_unkNownFields = pb::UnkNownFieldSet.MergeFrom(_unkNownFields,other._unkNownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom(pb::CodedInputStream input) {
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
input.ReadRawMessage(this);
#else
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
_unkNownFields = pb::UnkNownFieldSet.MergeFieldFrom(_unkNownFields,input);
break;
case 10: {
results_.AddEntriesFrom(input,_repeated_results_codec);
break;
}
}
}
#endif
}
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
_unkNownFields = pb::UnkNownFieldSet.MergeFieldFrom(_unkNownFields,ref input);
break;
case 10: {
results_.AddEntriesFrom(ref input,_repeated_results_codec);
break;
}
}
}
}
#endif
#region nested types
/// <summary>Container for nested types declared in the GetDataResponse message type.</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static partial class Types {
public sealed partial class keyvaluePair : pb::IMessage<keyvaluePair>
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE,pb::IBufferMessage
#endif
{
private static readonly pb::MessageParser<keyvaluePair> _parser = new pb::MessageParser<keyvaluePair>(() => new keyvaluePair());
private pb::UnkNownFieldSet _unkNownFields;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser<keyvaluePair> Parser { get { return _parser; } }
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pbr::MessageDescriptor Descriptor {
get { return global::GrpcService.Protos.GetDataResponse.Descriptor.nestedTypes[0]; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public keyvaluePair() {
OnConstruction();
}
partial void OnConstruction();
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public keyvaluePair(keyvaluePair other) : this() {
pairs_ = other.pairs_.Clone();
_unkNownFields = pb::UnkNownFieldSet.Clone(other._unkNownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public keyvaluePair Clone() {
return new keyvaluePair(this);
}
/// <summary>Field number for the "pairs" field.</summary>
public const int PairsFieldNumber = 1;
private static readonly pbc::MapField<string,string>.Codec _map_pairs_codec
= new pbc::MapField<string,string>.Codec(pb::FieldCodec.ForString(10,""),pb::FieldCodec.ForString(18,10);
private readonly pbc::MapField<string,string> pairs_ = new pbc::MapField<string,string>();
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public pbc::MapField<string,string> Pairs {
get { return pairs_; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override bool Equals(object other) {
return Equals(other as keyvaluePair);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool Equals(keyvaluePair other) {
if (ReferenceEquals(other,null)) {
return false;
}
if (ReferenceEquals(other,this)) {
return true;
}
if (!Pairs.Equals(other.Pairs)) return false;
return Equals(_unkNownFields,other._unkNownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override int GetHashCode() {
int hash = 1;
hash ^= Pairs.GetHashCode();
if (_unkNownFields != null) {
hash ^= _unkNownFields.GetHashCode();
}
return hash;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public override string ToString() {
return pb::JsonFormatter.ToDiagnosticString(this);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void Writeto(pb::CodedOutputStream output) {
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
output.WriterawMessage(this);
#else
pairs_.Writeto(output,_map_pairs_codec);
if (_unkNownFields != null) {
_unkNownFields.Writeto(output);
}
#endif
}
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
void pb::IBufferMessage.InternalWriteto(ref pb::WriteContext output) {
pairs_.Writeto(ref output,_map_pairs_codec);
if (_unkNownFields != null) {
_unkNownFields.Writeto(ref output);
}
}
#endif
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public int CalculateSize() {
int size = 0;
size += pairs_.CalculateSize(_map_pairs_codec);
if (_unkNownFields != null) {
size += _unkNownFields.CalculateSize();
}
return size;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom(keyvaluePair other) {
if (other == null) {
return;
}
pairs_.Add(other.pairs_);
_unkNownFields = pb::UnkNownFieldSet.MergeFrom(_unkNownFields,other._unkNownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void MergeFrom(pb::CodedInputStream input) {
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
input.ReadRawMessage(this);
#else
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
_unkNownFields = pb::UnkNownFieldSet.MergeFieldFrom(_unkNownFields,input);
break;
case 10: {
pairs_.AddEntriesFrom(input,_map_pairs_codec);
break;
}
}
}
#endif
}
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
_unkNownFields = pb::UnkNownFieldSet.MergeFieldFrom(_unkNownFields,ref input);
break;
case 10: {
pairs_.AddEntriesFrom(ref input,_map_pairs_codec);
break;
}
}
}
}
#endif
}
}
#endregion
}
解决方法
在 ASP.NET Core gRPC for WCF developers 中,Repeated fields for lists and arrays 部分解释了
重复字段由 Google.Protobuf.Collections.RepeatedField 类型的只读属性表示,而不是任何内置的 .NET 集合类型。此类型实现所有标准的 .NET 集合接口,例如 IList 和 IEnumerable。因此,您可以使用 LINQ 查询或轻松地将其转换为数组或列表。
您必须使用 Add
/AddRange
向该属性添加项目,例如:
var response=new GetDataResponse();
response.AddRange(results);
return response;
您不需要将 response
包装在 Task
中以返回它,因为您的方法具有 async
签名,因此它已经包装了返回值。无论如何,await Task.FromResult(x)
只返回 x