c# - BsonSerializationException al serializar un Diccionario<DateTime, T> a BSON
.net mongodb (2)
El problema es que el nuevo controlador serializa los diccionarios como un documento por defecto.
El controlador MongoDB C # tiene 3 formas de serializar un diccionario: Document
, ArrayOfArrays
y ArrayOfDocuments
( más sobre eso en la documentación ). Cuando se serializa como un documento, las claves del diccionario son los nombres del elemento BSON que tiene algunas limitaciones (por ejemplo, como sugiere el error, deben ser serializadas como cadenas).
En este caso, las claves del diccionario son DateTime
s que no se serializan como cadenas, sino como Date
s, por lo que debemos elegir otra representación de DictionaryRepresentation
.
Para cambiar la serialización de esta propiedad específica, podemos usar el atributo BsonDictionaryOptions
con una representación de DictionaryRepresentation
diferente:
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfArrays)]
public Dictionary<DateTime, int> Dictionary { get; private set; }
Sin embargo, tenemos que hacer eso en cada miembro problemático individualmente. Para aplicar esta DictionaryRepresentation
de DictionaryRepresentation
a todos los miembros relevantes, podemos implementar una nueva convención:
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));
}
}
Que registramos de la siguiente manera:
ConventionRegistry.Register(
"DictionaryRepresentationConvention",
new ConventionPack {new DictionaryRepresentationConvention(DictionaryRepresentation.ArrayOfArrays)},
_ => true);
Recientemente me he mudado al nuevo controlador MongoDB C # v2.0 del v1.9 en desuso .
Ahora, cuando serializo una clase que tiene un diccionario, a veces me encuentro con la siguiente BsonSerializationException
:
MongoDB.Bson.BsonSerializationException: cuando se usan los valores de clave de DocumentRepresentation.Document se deben serializar como cadenas.
Aquí hay una reproducción mínima:
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());
}
La respuesta anterior es excelente, pero desafortunadamente desencadena una excepción Exception en ciertas jerarquías de objetos recursivos; aquí hay una versión ligeramente mejorada y actualizada.
public class DictionaryRepresentationConvention : ConventionBase, IMemberMapConvention
{
private readonly DictionaryRepresentation _dictionaryRepresentation;
public DictionaryRepresentationConvention(DictionaryRepresentation dictionaryRepresentation = DictionaryRepresentation.ArrayOfDocuments)
{
// see http://mongodb.github.io/mongo-csharp-driver/2.2/reference/bson/mapping/#dictionary-serialization-options
_dictionaryRepresentation = dictionaryRepresentation;
}
public void Apply(BsonMemberMap memberMap)
{
memberMap.SetSerializer(ConfigureSerializer(memberMap.GetSerializer(),Array.Empty<IBsonSerializer>()));
}
private IBsonSerializer ConfigureSerializer(IBsonSerializer serializer, IBsonSerializer[] stack)
{
if (serializer is IDictionaryRepresentationConfigurable dictionaryRepresentationConfigurable)
{
serializer = dictionaryRepresentationConfigurable.WithDictionaryRepresentation(_dictionaryRepresentation);
}
if (serializer is IChildSerializerConfigurable childSerializerConfigurable)
{
if (!stack.Contains(childSerializerConfigurable.ChildSerializer))
{
var newStack = stack.Union(new[] { serializer }).ToArray();
var childConfigured = ConfigureSerializer(childSerializerConfigurable.ChildSerializer, newStack);
return childSerializerConfigurable.WithChildSerializer(childConfigured);
}
}
return serializer;
}