Saltar al contenido

Solución alternativa para serializar y deserializar la estructura en MongoDB

Recabamos por diferentes espacios para así traerte la solución a tu dilema, en caso de alguna duda déjanos la duda y te respondemos porque estamos para servirte.

Solución:

Finalmente encontré la solución, está entre la solución original 2 y 3.

La idea principal es encontrar toda la estructura en la estructura de datos del “cliente” y registrar el serializador de estructuras especiales para ello. Los desafíos son los siguientes:

Encuentra todo el tipo de estructura en la estructura de datos “cliente”

Hay que buscarlo de forma recursiva, incluso la estructura es parte de la colección que está oculta en la clase que está cubierta en colecciones, etc … Así que tuvimos que encontrarla en todos los casos. Afortunadamente, MongoDB ayuda a encontrar todas las instancias, porque durante la serialización, MongoDB realiza un recorrido recursivo en cada tipo. Así que registramos un proveedor de serialización que “detecta” toda la estructura y le da un serializador especial.

Detecta que el tipo dado es estructura o no

Para hacer este trabajo, hubo muchas respuestas en StackOverflow, needer de ellos era perfecto. Quizás mi solución tampoco sea perfecta, pero hicimos una unión de todas las ideas. Entonces verificamos que el tipo no es primitivo, no es enum, pero es un tipo de valor, y no una estructura predeterminada, que ya tiene un serializador en MongoDB.


Los códigos son los siguientes:

1, registre un proveedor de serializador para MongoDB:

BsonSerializer.RegisterSerializationProvider( new MongoDB_SerializationProvider() );

2, implementar un serializador:

class MongoDB_SerializationProvider : BsonSerializationProviderBase

    private static readonly object locker = new object();
    private static Dictionary _StructSerializers;
    private static MongoDB_DecimalSerializer _DecimalSerializer;


    static MongoDB_SerializationProvider()
    
        _StructSerializers = new Dictionary();
        _DecimalSerializer = new MongoDB_DecimalSerializer();
    

    public override IBsonSerializer GetSerializer( Type type, IBsonSerializerRegistry serializerRegistry )
    
        if ( type == typeof( decimal ) )
        
            return _DecimalSerializer;
        
        else if ( Reflection.Info.IsStruct( type ) && type != typeof( ObjectId ) )
        
            MongoDB_StructSerializer structSerializer = null;

            lock ( locker )
            
                if ( _StructSerializers.TryGetValue( type, out structSerializer ) == false )
                
                    structSerializer = new MongoDB_StructSerializer( type );
                    _StructSerializers.Add( type, structSerializer );
                
            

            return structSerializer;
        
        else
        
            return null;
        
    

La parte decimal es otro tema interesante, pero no forma parte de la pregunta actual. Una cosa debemos tener cuidado: el ObjectId de MongoDB también es una estructura y, por supuesto, no queremos registrar un serializador para ObjectId-s. Hay una función en el código, que hace un poco de magia: Reflection.Info.IsStruct( type ) Aquí está el código:

    public static bool IsStruct( Type type )
    
        if ( IsPrimitiveType( type ) == true )
            return false;

        if ( type.IsValueType == false )
            return false;

        return true;
    

    static public bool IsPrimitiveType( Type type )
    
        if ( type.GetTypeInfo().IsPrimitive == true )
            return true;

        if ( type.GetTypeInfo().IsEnum == true )
            return true;

        if ( type == typeof( decimal ) )
            return true;

        if ( type == typeof( string ) )
            return true;

        if ( type == typeof( DateTime ) )
            return true;

        if ( type == typeof( DateTimeOffset ) )
            return true;

        if ( type == typeof( TimeSpan ) )
            return true;

        if ( type == typeof( Guid ) )
            return true;

        return false;
    

3, implementar el serializador

Es un código un poco más largo, pero espero que aún sea comprensible:

public class MongoDB_StructSerializer : IBsonSerializer

    public Type ValueType  get; 

    public MongoDB_StructSerializer( Type valueType )
    
        ValueType = valueType;
    

    public void Serialize( BsonSerializationContext context, BsonSerializationArgs args, object value )
    
        if ( value == null )
        
            context.Writer.WriteNull();
        
        else
        
            List members = Reflection.Serialize.GetAllSerializableMembers( ValueType );

            context.Writer.WriteStartDocument();
            foreach( MemberInfo member in members )
            
                context.Writer.WriteName( member.Name );
                BsonSerializer.Serialize( context.Writer, Reflection.Info.GetMemberType( member ), Reflection.Info.GetMemberValue( member, value ), null, args );
            
            context.Writer.WriteEndDocument();
        
    

    public object Deserialize( BsonDeserializationContext context, BsonDeserializationArgs args )
    
        BsonType bsonType = context.Reader.GetCurrentBsonType();
        if ( bsonType == BsonType.Null )
        
            context.Reader.ReadNull();
            return null;
        
        else
        
            object obj = Activator.CreateInstance( ValueType );

            context.Reader.ReadStartDocument();

            while ( context.Reader.ReadBsonType() != BsonType.EndOfDocument )
            
                string name = context.Reader.ReadName();

                FieldInfo field = ValueType.GetField( name );
                if ( field != null )
                
                    object value = BsonSerializer.Deserialize( context.Reader, field.FieldType );
                    field.SetValue( obj, value );
                

                PropertyInfo prop = ValueType.GetProperty( name );
                if ( prop != null )
                
                    object value = BsonSerializer.Deserialize( context.Reader, prop.PropertyType );
                    prop.SetValue( obj, value, null );
                
            

            context.Reader.ReadEndDocument();

            return obj;
        
    

La función mágica: Reflection.Serialize.GetAllSerializableMembers Contiene algunas cosas realmente interesantes, qué es miembro serializable y qué no.

    public static List GetSerializableMembers( Type type, BindingFlags bindingFlags )
    
        List list = new List();

        FieldInfo[] fields = type.GetFields( bindingFlags );
        foreach ( FieldInfo field in fields )
        
            if ( IsFieldSerializable( type, field ) == false )
                continue;

            list.Add( field );
        

        PropertyInfo[] properties = type.GetProperties( bindingFlags );
        foreach ( PropertyInfo property in properties )
        
            if ( IsPropertySerializable( type, property ) == false )
                continue;

            list.Add( property );
        

        return list;
    

    public static bool IsFieldSerializable( Type type, FieldInfo field )
    
        if ( field.IsInitOnly == true )
            return false;

        if ( field.IsLiteral == true )
            return false;

        if ( field.IsDefined( typeof( CompilerGeneratedAttribute ), false ) == true )
            return false;

        if ( field.IsDefined( typeof( IgnoreAttribute ), false ) == true )
            return false;

        return true;
    

    public static bool IsPropertySerializable( Type type, PropertyInfo property )
    
        if ( property.CanRead == false )
            return false;

        if ( property.CanWrite == false )
            return false;

        if ( property.GetIndexParameters().Length != 0 )
            return false;

        if ( property.GetMethod.IsVirtual && property.GetMethod.GetBaseDefinition().DeclaringType != type )
            return false;

        if ( property.IsDefined( typeof( IgnoreAttribute ), false ) == true )
            return false;

        return true;
    

Resumen

Estas soluciones se probaron bien (alrededor de 15 a 20 casos de prueba diferentes) y funcionan bien. Creo que la comunidad de MongoDB también puede implementar la serialización de estructuras. Lamentan que no se pueda hacer, porque las estructuras son valutypes, por eso los valores se copian, no la referencia, así que cuando una función cambia el valor dentro, el original no cambia. ¡Pero! Todo el código de serialización dentro del ‘objeto’ y las estructuras de MongoDB Uses también son objetos. Y en ninguna parte del código del controlador, no hay cambios de miembros. Solo en deserialización, que se sobrescribe en nuestro código.

¡Entonces la comunidad de MongoDB puede hacerlo, si lo desea! 🙂

PD: que leas el post largo, aquí hay una patata

Valoraciones y reseñas

Acuérdate de que tienes la capacidad de interpretar tu experiencia si te ayudó.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *