27

最近、非推奨の v1.9から新しい MongoDB C# ドライバー v2.0に移行しました。

さて、辞書を持つクラスをシリアル化すると、次のような問題が発生することがありますBsonSerializationException

MongoDB.Bson.BsonSerializationException: DictionaryRepresentation.Document を使用する場合、キー値は文字列としてシリアル化する必要があります。

最小限の再現は次のとおりです。

class Hamster
{
    public ObjectId Id { get; private set; }
    public Dictionary<DateTime,int> Dictionary { get; private set; }
    public Hamster()
    {
        Id = ObjectId.GenerateNewId();
        Dictionary = new Dictionary<DateTime, int>();
        Dictionary[DateTime.UtcNow] = 0;
    }
}

static void Main()
{
    Console.WriteLine(new Hamster().ToJson());
}
4

3 に答える 3

53

問題は、新しいドライバーが既定で辞書をドキュメントとしてシリアル化することです。

MongoDB C# ドライバーには、辞書をシリアル化する 3 つの方法があります: DocumentArrayOfArrays& ArrayOfDocuments(詳細についてはドキュメントを参照してください)。ドキュメントとしてシリアル化する場合、ディクショナリ キーは BSON 要素の名前であり、いくつかの制限があります (たとえば、エラーが示唆するように、文字列としてシリアル化する必要があります)。

この場合、ディクショナリのキーはDateTime、文字列としてシリアル化されていない s ですが、Dates であるため、別の を選択する必要がありますDictionaryRepresentation

この特定のプロパティのシリアライゼーションを変更するにはBsonDictionaryOptions、別の属性で属性を使用できDictionaryRepresentationます。

[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
public Dictionary<DateTime, int> Dictionary { get; private set; }

ただし、問題のあるすべてのメンバーに対して個別にそれを行う必要があります。これをDictionaryRepresentation関連するすべてのメンバーに適用するために、新しい規則を実装できます。

class DictionaryRepresentationConvention : ConventionBase, IMemberMapConvention
{
    private readonly DictionaryRepresentation _dictionaryRepresentation;
    public DictionaryRepresentationConvention(DictionaryRepresentation dictionaryRepresentation)
    {
        _dictionaryRepresentation = dictionaryRepresentation;
    }
    public void Apply(BsonMemberMap memberMap)
    {
        memberMap.SetSerializer(ConfigureSerializer(memberMap.GetSerializer()));
    }
    private IBsonSerializer ConfigureSerializer(IBsonSerializer serializer)
    {
        var dictionaryRepresentationConfigurable = serializer as IDictionaryRepresentationConfigurable;
        if (dictionaryRepresentationConfigurable != null)
        {
            serializer = dictionaryRepresentationConfigurable.WithDictionaryRepresentation(_dictionaryRepresentation);
        }

        var childSerializerConfigurable = serializer as IChildSerializerConfigurable;
        return childSerializerConfigurable == null
            ? serializer
            : childSerializerConfigurable.WithChildSerializer(ConfigureSerializer(childSerializerConfigurable.ChildSerializer));
    }
} 

次のように登録します。

ConventionRegistry.Register(
    "DictionaryRepresentationConvention",
    new ConventionPack {new DictionaryRepresentationConvention(DictionaryRepresentation.ArrayOfArrays)},
    _ => true);
于 2015-01-23T14:16:06.503 に答える