2011-06-07 19 views
5

Estoy usando el controlador oficial MongoDb C#.Recuperar instancias heredadas de MongoDB usando C#

Mi escenario: Almaceno objetos en MongoDb. Todos los objetos son instancias de clases que heredan de la misma clase de raíz. En el momento del diseño, no conozco todas las clases que pueden almacenarse (es decir, pueden estar conectadas), así que necesito una forma de decirle al serializador/controlador cómo asignar las clases a los documentos (descriminators en el documento).

¿Alguien tiene alguna idea?

Respuesta

8

El controlador oficial C# escribirá un valor de discriminador "_t" siempre que el tipo real de un objeto sea diferente del tipo nominal. Así, por ejemplo:

MyRootClass obj = new MyDerivedClass(); 
collection.Insert(obj); 

La sentencia Insert también podría haberse escrito:

collection.Insert<MyRootClass>(obj); 

pero es más fácil dejar que el compilador inferir el parámetro de tipo.

Dado que el tipo real de obj es diferente del tipo nominal, se escribirá el discriminador "_t".

Al leer hacia atrás el objeto que tendrá que asegurarse de que MyDerivedClass se ha registrado correctamente:

BsonClassMap.RegisterClassMap<MyDerivedClass>(); 

o el serializador no reconocerá el discriminador (esto puede parecer como una restricción, pero es lógico que el serializador solo puede funcionar con tipos que conoce).

Mencionó que no conoce las clases en tiempo de compilación, por lo que el código de registro anterior debe invocarse dinámicamente. Una forma de hacerlo es:

Type myDerivedClass; // your plugged-in class 
var registerClassMapDefinition = typeof(BsonClassMap).GetMethod("RegisterClassMap", new Type[0]); 
var registerClassMapInfo = registerClassMapDefinition.MakeGenericMethod(myDerivedClass); 
registerClassMapInfo.Invoke(null, new object[0]); 

Técnicamente, la serialización no está utilizando la reflexión; es impulsado por metadatos. La reflexión se usa una vez para construir el mapa de clase, pero después de eso, el mapa de clase se usa directamente sin reflexión, y la sobrecarga es bastante baja.

+0

Gracias mucho. Exactamente lo que estaba buscando. –

+0

Muy buena respuesta, la única que encontré completa para deserializar clases desconocidas en tiempo de compilación. –

0

Eche un vistazo a la documentación de serialización de controladores here.

+0

Hmm .. No veo cómo hacerlo sin reflexión con tipos desconocidos. – mnemosyn

1

Escribí una clase de ayuda mejorando la excelente respuesta de Robert Stam y permitiendo los mismos parámetros que el método estático BsonClassMap.RegisterClassMap < ...>().

public class MyBsonClassMap 
{ 
    public static void RegisterClassMap(Type type) 
    { 
     Type bsonClassMapType = typeof(BsonClassMap<>).MakeGenericType(new Type[] { type }); 
     BsonClassMap bsonClassMap = (BsonClassMap)Activator.CreateInstance(bsonClassMapType); 
     BsonClassMap.RegisterClassMap(bsonClassMap); 
    } 

    public static void RegisterClassMap(Type type, Action<BsonClassMap> classMapInitializer) 
    { 
     Type bsonClassMapType = typeof(BsonClassMap<>).MakeGenericType(new Type[] { type }); 
     BsonClassMap bsonClassMap = (BsonClassMap)Activator.CreateInstance(bsonClassMapType); 
     classMapInitializer(bsonClassMap); 
     BsonClassMap.RegisterClassMap(bsonClassMap); 
    } 
} 

ahora soy capaz de registrar un tipo que era desconocido en tiempo de compilación con casi la misma sintaxis que uno conocido:

Type unknownType; // is the type that was unknown at compile time 
MyBsonClassMap.RegisterClassMap(unknownType); 

o

MyBsonClassMap.RegisterClassMap(unknownType, cm => 
    cm.AutoMap()); 

Estos métodos deben ser disponible en el controlador C#.

Cuestiones relacionadas