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