【问题标题】:Serializing base class properties序列化基类属性
【发布时间】:2021-05-01 03:34:00
【问题描述】:

所以,如果我有:

  [ProtoContract]
  public abstract class BaseRequest
  {
     [ProtoMember(1)] public Guid Guid { get; set; }
  }

  [ProtoContract]
  public class Request : BaseRequest
  {
     [ProtoMember(1)] public long Id { get; set; }
  }

我尝试序列化 Request 和反序列化 BaseRequest,它不起作用。它不知道具体的类是什么。我需要添加一个[ProtoInclude]。这对我来说很有意义。

我看到的是,如果我序列化请求并反序列化请求,那也不起作用,我认为这是出乎意料的。我希望序列化程序已经知道在这种情况下工作所需的一切。我需要在 BaseRequest 中包含[ProtoInclude],即使我要序列化的只是请求。

我遇到麻烦的地方是我在一个库中定义了一个 BaseRequest 并且该库的使用者需要从它继承。基本上,一种特定类型的请求必须附加数据——除了转储继承并将该代码复制/粘贴到每个子类中之外,是否有这种模式?

【问题讨论】:

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


    【解决方案1】:

    为了让你可以序列化Request和反序列化BaseRequest等,它从最基础的类型开始实现继承,向更多的派生类型发展;因此,如果这是 xml,它将是:

    <BaseType>
        <BaseTypeField1/>
        //...
        <--- at most one, possibly none, of the following -- >
        <SubType1>...</SubType1>
        <SubType2>...</SubType2>
    </BaseType>
    

    它需要在第一次尝试接触继承模型中的任何类型时建立对 BaseType 的这种理解。现在,发现你的基类型很容易,但是通过反射发现任何类型的每个可能的派生类型真的很困难,因此为什么 ProtoInclude 需要在基类型上,而不是派生类型上。


    如果您可以提供可靠、一致的字段编号到子类型的映射,则所有这些都可以在运行时配置而不是通过 ProtoInclude,但是:您需要提供和管理自己的注册表的子类型。如果你有这个,我可以向你展示如何以更好的方式配置模型。

    【讨论】:

    • 好吧,有趣。我认为我们的用例是这样的:某些调用需要附加他们的客户信息。因此,客户端向 guid 注册,然后将其包含在后续调用中。通常在 C# 中完成这种事情的方式是从 BaseRequest 继承,但 GRPC 似乎并不喜欢这种方式……我们应该使用更好的模式吗? (在下一条评论中继续)
    • 这个想法是尝试简化客户端的库使​​用,因此连接由库完成(包括向 guid 注册),如果您只是要求从 BaseRequest 继承的请求,那就是基本上免费获得此功能的一种非常简单的方法。我想我们也可以要求所有请求都实现,比如说,一个 IBaseRequest 接口,它上面有一个 guid,但这并不容易使用。
    • @foof 最终,protobuf / gRPC 不直接支持继承;我们在 protobuf-net 中做了一些工作以使其工作,但我们不能做不可能的事情。 Protobuf 字段标识符不是 guid,它们是整数。我需要一种可靠的方法来为每个子类型获取一个唯一且可重复的整数。
    【解决方案2】:

    我想出了一个办法,而且我认为它会奏效,尽管感觉应该有更好的办法:

    [ProtoContract]
    public abstract class BaseRequest
    {
       public static int NextSubType = 1000;
       public static ConcurrentDictionary<Type, int> Initialized = new ConcurrentDictionary<Type, int>();
       public static object SyncObject = new object();
     
       [ProtoMember(1)] public Guid Guid { get; set; }
    
       protected BaseRequest()
       {
          var type = this.GetType();
    
          if (!Initialized.ContainsKey(type))
          {
             lock (SyncObject)
             {
                if (!Initialized.ContainsKey(type))
                {
                   var next = Interlocked.Increment(ref BaseRequest2.NextSubType);
                   RuntimeTypeModel.Default.Add(typeof(BaseRequest2), true).AddSubType(next, type);
                   Initialized[type] = next;
                }
             }
          }
       }
    }
    
    [ProtoContract]
    public class Request : BaseRequest
    {
       [ProtoMember(1)] public long Id { get; set; }
    }
    

    【讨论】:

    • 这将在不同的运行中给出不同的键,具体取决于尝试类型的顺序。这意味着它不可靠 - 您将无法在不同进程、不同机器、不同运行等之间使用数据
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多