【问题标题】:Error when migrating WCF service to gRPC service将 WCF 服务迁移到 gRPC 服务时出错
【发布时间】:2021-04-17 04:32:03
【问题描述】:

我有一个想要重写为 gRPC 服务的 WCF 服务。有一个特定的端点给我带来了一些麻烦。现在该方法如下所示:

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>> results = new 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

  }

【问题讨论】:

  • 在问题本身中将错误发布为 text。图片无法复制和搜索。现在没有问题,只是一些可能编译也可能不编译的代码
  • await Task.FromResult(new ?为什么不返回new GetDataResponse()
  • 请发布GetDataResponse的代码。错误表明GetDataResponse 是只读属性。
  • @PanagiotisKanavos 我相信new GetDataResponse() 应该没问题。我刚刚关注了gRPC services with C#,其中使用了Task.FromResult()。 GetDataResponse 类由协议缓冲区编译器自动生成
  • 错误说不是。贴出代码。您不需要 await Task.FromResult 只是为了返回一个值。文档中的代码示例不是async 方法,因此它必须将返回值包装在Task中

标签: protocol-buffers .net-5 grpc-c#


【解决方案1】:

ASP.NET Core gRPC for WCF developersRepeated 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

【讨论】:

  • @PanagiotsKanavos 错过了示例中的方法不是async,因此需要Task.FromResult()。我试过你的答案,它有效。感谢您的时间和精力
  • @PanagiotisEfthymiou 请注意文档示例。很多时候,他们试图在一个页面中展示多个东西,导致混乱。文档示例仅用于显示模拟响应,但并未这么说。另一方面,这只是一个Overview 页面。一个更好的例子是Tutorial: Create a gRPC client and server in ASP.NET Core
  • 如果您想查看一些真正令人困惑的文档,请查看Background tasks with hosted services in ASP.NET Core。这将 any 应用程序可以使用的 BackgroundService 类与特定项目模板和使用演示代码的特定实现示例混合在一起...... pub/sub。还有一个关于从单例调用作用域对象的部分。在这一切中,它忘记清楚地说如果你想创建一个 web 服务,你需要更改 SDK 类型
  • 如果您还不了解所涉及的所有技术,您将无法说出什么是相关的,什么是不相关的
  • 前段时间,我查看了那个特定的文档页面。过了一会儿我迷路了。无论如何,如果我将来遇到无法解决的案件,我可以联系您吗?
猜你喜欢
  • 2012-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多