2018年5月7日月曜日

Protocol Buffers オブジェクトを MongoDB に格納する 2

Protocol Buffers の IDL で定義された型のオブジェクトを MongoDB に格納できるかどうかを検証しています。前回 に続き、今回は Map や Any について調べました。
MongoDB 公式の C# 実装である MongoDB.Driver (2.5.1) を使用しています。

 

Map も setter が必要


Grpc.Tools では Map は getter のみの MapField 型のプロパティとして定義されます。そのため repeated のときと同様、UpdateMany メソッドなどで "Unable to determine the serialization information" というエラーが発生します。

MapField 型自体はシリアライズ可能です。setter を持つプロパティを定義すれば格納できます。setter のスコープは private でもOKです。

Grpc.Toolsで自動生成されたコード
public MapField<string, string> Metadata {
  get { return metadata_; }
}

partial ファイルで追加したコード
[BsonElement("Metadata")]
public MapField<string, string> MetadataSerializable {
  get { return Metadata; }
  private set {
    Metadata.Clear();
    if (value!=null) { Metadata.Add(value); }
  }
}
この場合、FilterDefinition や UpdateDefinition で Metadata プロパティの代わりに MetadataSerializable プロパティを使うことになります。


Any はデシリアライズ不可


Any のフィールドが定義された型を格納することはできますが、Find メソッドなどで取得しようとすると "Type 'Google.Protobuf.ByteString' does not have a suitable constructor or Add method." というエラーが発生します。C# の Any 型は String 型の TypeUrl プロパティと ByteString 型の Value プロパティで構成されますが、このByteString 型にデシリアライズ用のコンストラクタがないということです。

BSON を用いればデシリアライズできると思いますが、Find などタイプセーフなジェネリックメソッドを用いたい場合、Any の Pack, Unpack を使って本来の型に戻すようなプロパティを定義することになるでしょうか。

さらに、Any には Map や repeated とは異なる点があります。Any のプロパティには setter が定義されています。そのため、InsertMany メソッドなど格納対象のフィールドを指定しないメソッドでは格納対象になります。それを Find メソッドなどで取得しようとするとデシリアライズに失敗します。UpdateMany メソッドなど格納対象のフィールドを明示的に指定するメソッドを使って、Any のプロパティを格納対象から除外しておくような工夫が要りそうです。

MongoDB に格納しようとしている型に Any を用いるのはあまり実用的ではないように思えます。


インターフェースを指定しても実際の型の情報が使用される


InsertMany メソッドの型引数にインターフェースを指定した場合、インターフェースの型情報ではなく実際の型情報が用いられます。インターフェースに定義されたプロパティのみが格納対象になるのであれば上記の Any の問題の回避方法の一つになると考えたのですが、ダメなようです。
なお、インターフェースを指定して格納した場合、_t というフィールドが生成されて格納されたオブジェクトの実際の型名が格納されるようです。

また、UpdateMany メソッドでは、"{document}.Id is not supported." というエラーが発生して失敗します。インターフェースと実装型の両方に Id という名前のプロパティを定義していても失敗します。メソッドによって型定義周りの実装に違いがあるようで、非常に分かりにくいです。


0 件のコメント:

コメントを投稿

paiza のスキルチェックをやってみました

いまさら感はありますが、 paiza のスキルチェックをやってみました。指定された時間内にコードを書いてユニットテストにかけ、その結果を基に評価を数値化してくれるというものですが、ゲーム感覚で空き時間を見つけて進めていこうと考えています。 どうやら時間が短いほど高い評価を得...