enum description clase c# mongodb mongodb-.net-driver

c# - description - Almacenamiento de Enums como cadenas en MongoDB



enum c# (7)

¿Hay alguna manera de almacenar Enums como nombres de cadena en lugar de valores ordinales?

Ejemplo:

Imagina que tengo esta enumeración:

public enum Gender { Female, Male }

Ahora si existe un usuario imaginario con

... Gender gender = Gender.Male; ...

se almacenará en la base de datos MongoDb como {... "Género": 1 ...}

pero preferiría algo como esto {... "Género": "Hombre" ...}

es posible? Mapeo personalizado, trucos de reflexión, lo que sea.

Mi contexto: utilizo colecciones fuertemente tipadas sobre POCO (bueno, marco AR y uso el polimorfismo de vez en cuando). Tengo una delgada capa de abstracción de acceso a datos en forma de Unidad de trabajo. Así que no estoy serializando / deserializando cada objeto, pero puedo (y lo hago) definir algunos ClassMaps. Uso el controlador oficial MongoDb + fluent-mongodb.


Con el controlador 2.x lo resolví usando un serializador específico :

BsonClassMap.RegisterClassMap<Person>(cm => { cm.AutoMap(); cm.MapMember(c => c.Gender).SetSerializer(new EnumSerializer<Gender>(BsonType.String)); });


El controlador MongoDB .NET le permite aplicar convenciones para determinar cómo se manejan ciertas asignaciones entre los tipos de CLR y los elementos de la base de datos.

Si desea que esto se aplique a todas sus enum, solo tiene que configurar convenciones una vez por Dominio de aplicación (generalmente al iniciar su aplicación), en lugar de agregar atributos a todos sus tipos o mapear manualmente cada tipo:

// Set up MongoDB conventions var pack = new ConventionPack { new EnumRepresentationConvention(BsonType.String) }; ConventionRegistry.Register("EnumStringConvention", pack, t => true);


He descubierto que la aplicación de la respuesta de Ricardo Rodríguez no es suficiente en algunos casos para serializar adecuadamente los valores enum para encadenar en MongoDb:

// Set up MongoDB conventions var pack = new ConventionPack { new EnumRepresentationConvention(BsonType.String) }; ConventionRegistry.Register("EnumStringConvention", pack, t => true);

Si su estructura de datos implica que los valores enumerados se encapsulan en objetos, la serialización MongoDb no usará el conjunto EnumRepresentationConvention para serializarlo.

De hecho, si observa la implementación del ObjectSerializer del controlador ObjectSerializer , resolverá el TypeCode del valor TypeCode ( Int32 para valores enum) y usará ese tipo para almacenar su valor enum en la base de datos. Entonces los valores enum encuadrados terminan siendo serializados como valores int . También permanecerán como valores int al ser deserializados.

Para cambiar esto, es posible escribir un ObjectSerializer personalizado que ObjectSerializer el conjunto EnumRepresentationConvention si el valor en caja es una enumeración. Algo como esto:

public class ObjectSerializer : MongoDB.Bson.Serialization.Serializers.ObjectSerializer { public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { var bsonWriter = context.Writer; if (value != null && value.GetType().IsEnum) { var conventions = ConventionRegistry.Lookup(value.GetType()); var enumRepresentationConvention = (EnumRepresentationConvention) conventions.Conventions.FirstOrDefault(convention => convention is EnumRepresentationConvention); if (enumRepresentationConvention != null) { switch (enumRepresentationConvention.Representation) { case BsonType.String: value = value.ToString(); bsonWriter.WriteString(value.ToString()); return; } } } base.Serialize(context, args, value); } }

y luego configure el serializador personalizado como el que se usará para serializar objetos:

BsonSerializer.RegisterSerializer(typeof(object), new ObjectSerializer());

Hacer esto asegurará que los valores de enum en caja se almacenen como cadenas al igual que las que no están en la casilla.

Tenga en cuenta, sin embargo, que al deserializar su documento, el valor en caja seguirá siendo una cadena. No se convertirá de nuevo al valor enum original. Si necesita convertir la cadena de nuevo al valor enum original, es probable que tenga que agregar un campo de discriminación en el documento para que el serializador pueda saber en qué tipo de enumeración se debe inicializar.

Una forma de hacerlo sería almacenar un documento bson en lugar de solo una cadena, en la que el campo de discriminación ( _t ) y un campo de valor ( _v ) se usarían para almacenar el tipo de enumeración y su valor de cadena.


Puede personalizar el mapa de clase para la clase que contiene la enumeración y especificar que el miembro esté representado por una cadena. Esto manejará tanto la serialización como la deserialización de la enumeración.

if (!MongoDB.Bson.Serialization.BsonClassMap.IsClassMapRegistered(typeof(Person))) { MongoDB.Bson.Serialization.BsonClassMap.RegisterClassMap<Person>(cm => { cm.AutoMap(); cm.GetMemberMap(c => c.Gender).SetRepresentation(BsonType.String); }); }

Todavía estoy buscando una forma de especificar que las enumeraciones se representen globalmente como cadenas, pero este es el método que estoy usando actualmente.


Terminé asignando valores a elementos enum, como lo sugirió Chris Smith en un comentario:

Lo evitaría. El valor de cadena ocupa mucho más espacio que un entero. Sin embargo, si persiste la persistencia, dé valores determinísticos a cada elemento de su enumeración, por lo que Female = 1 , Male = 2 modo que si la enumeración se agrega más tarde o cambia el orden de los elementos, no terminará con problemas.

No es exactamente lo que estaba buscando, pero parece que no hay otra alternativa.


Use MemberSerializationOptionsConvention para definir una convención sobre cómo se guardará una enumeración.

new MemberSerializationOptionsConvention(typeof(Gender), new RepresentationSerializationOptions(BsonType.String))


using MongoDB.Bson; using Newtonsoft.Json; using Newtonsoft.Json.Converters; public class Person { [JsonConverter(typeof(StringEnumConverter))] // JSON.Net [BsonRepresentation(BsonType.String)] // Mongo public Gender Gender { get; set; } }