Overview
Los objetos Polimórficos heredan propiedades y métodos de una o más clases padre. Estos objetos requieren una asignación especial para garantizar que el controlador .NET/C# los serialice correctamente hacia y desde documentos BSON.
Esta guía explica lo siguiente:
Cómo deserializar tipos polimórficos
Las convenciones de discriminador incluidas con el controlador .NET/C#
Cómo crear convenciones de discriminador personalizadas
Los ejemplos de esta página utilizan la siguiente jerarquía de herencia:
public class Animal { } public class Cat : Animal { } public class Dog : Animal { } public class Lion : Cat { } public class Tiger : Cat { }
Deserializar objetos polimórficos
Antes de que el serializador pueda deserializar cualquier objeto polimórfico, debe documentar la relación de todas las clases en la jerarquía de herencia.
Si estás utilizando el automapper para mapear tus clases, aplica el [BsonKnownTypes]
atributo a cada clase base en la jerarquía. Pasa cada clase que se herede directamente de la clase base como argumento.
El siguiente ejemplo muestra cómo aplicar el atributo [BsonKnownTypes] a las clases en la jerarquía de ejemplo Animal:
[] public class Animal { } [] public class Cat : Animal { } public class Dog : Animal { } public class Lion : Cat { } public class Tiger : Cat { }
Nota
Uso de BsonKnownTypes
Aplica el atributo [BsonKnownTypes] solo a las clases principales. Pasa como argumentos solo los tipos que heredan directamente de la clase, no todas las clases hijo en la jerarquía.
Si estás creando un mapa de clases manualmente, llama al método BsonClassMap.RegisterClassMap<T>() para cada clase en la jerarquía, como se muestra en el siguiente ejemplo:
BsonClassMap.RegisterClassMap<Animal>(); BsonClassMap.RegisterClassMap<Cat>(); BsonClassMap.RegisterClassMap<Dog>(); BsonClassMap.RegisterClassMap<Lion>(); BsonClassMap.RegisterClassMap<Tiger>();
Tip
Mapas de clase
Para obtener más información sobre cómo mapear clases, consulta la Personaliza la documentación de asignación de clases.
Usa discriminadores
En MongoDB, un discriminador es un campo agregado a un documento para identificar la clase a la que se deserializa el documento. Cuando una colección contiene más de un tipo de una única jerarquía de herencia, los discriminadores garantizan que cada documento se deserialice en la clase correcta. El controlador .NET/C# almacena el valor del discriminador en un campo llamado _t en el documento BSON. Generalmente, _t es el segundo campo en el documento BSON después de _id.
Las convenciones de discriminador definen el valor almacenado en el campo de discriminador. En esta sección, puedes aprender sobre las convenciones de discriminador que se incluyen con el .NET/C# Driver y cómo crear convenciones de discriminador personalizadas.
Convención de Discriminador Escalar
De forma predeterminada, el controlador de .NET/C# utiliza el ScalarDiscriminatorConvention. Según esta convención, el controlador .NET/C# asigna el valor del campo _t al nombre de la clase desde la cual se serializó el documento.
Suponga que crea una instancia de la clase de ejemplo Animal y de cada una de sus subclases. Si serializas estos objetos en una sola colección, el controlador .NET/C# aplica el ScalarDiscriminatorConvention y los documentos BSON correspondientes aparecen como sigue:
{ _id: ..., _t: "Animal", ... } { _id: ..., _t: "Cat", ... } { _id: ..., _t: "Dog", ... } { _id: ..., _t: "Lion", ... } { _id: ..., _t: "Tiger", ... }
El ScalarDiscriminatorConvention utiliza valores de discriminador concisos, pero puede ser difícil ejecutar una consulta sobre ellos. Por ejemplo, para encontrar todos los documentos de tipo o subtipo Cat, debes enumerar explícitamente cada clase que buscas:
var query = coll.AsQueryable().Where( item => item.GetType() == typeof(Cat) || item.GetType() == typeof(Lion) || item.GetType() == typeof(Tiger));
Convención discriminadora jerárquica
Para simplificar las consultas en su colección de tipos polimórficos, puede usar el HierarchicalDiscriminatorConvention. De acuerdo con esta convención, el valor de _t es un arreglo de todas las clases en la jerarquía de herencia del tipo de documento.
Para usar el HierarchicalDiscriminatorConvention, etiqueta la clase base de tu jerarquía de herencia como la clase raíz. Si estás utilizando el mapeador automático, etiqueta la clase raíz aplicando el atributo [BsonDiscriminatorAttribute] a la clase y pasando RootClass = true como argumento.
Nota
Debes especificar los discriminadores jerárquicos en el orden de raíz a hoja. Si se invierte el orden, el Driver .NET/C# no puede resolver el tipo concreto correcto.
El siguiente ejemplo de código etiqueta la clase Animal como la raíz de la jerarquía de herencia de ejemplo:
[] [] public class Animal { }
Si estás creando un mapa de clase manualmente, llama al método SetIsRootClass() y pasa true como argumento cuando registres el mapa de clase para la clase raíz. El siguiente ejemplo de código registra mapas de clases para las cinco clases de ejemplo, pero solo etiqueta la clase Animal como la raíz de la jerarquía de herencia:
BsonClassMap.RegisterClassMap<Animal>(classMap => { classMap.AutoMap(); classMap.SetIsRootClass(true); }); BsonClassMap.RegisterClassMap<Cat>(); BsonClassMap.RegisterClassMap<Dog>(); BsonClassMap.RegisterClassMap<Lion>(); BsonClassMap.RegisterClassMap<Tiger>();
Supón que etiquetas la clase de ejemplo Animal como la raíz de la jerarquía de herencia y luego creas una instancia de la clase Animal y de cada una de sus subclases. Si serializa estos objetos en una sola colección, el driver de .NET/C# aplica el HierarchicalDiscriminatorConvention y los documentos BSON correspondientes aparecen de la siguiente manera:
{ _id: ..., _t: "Animal", ... } { _id: ..., _t: ["Animal", "Cat"], ... } { _id: ..., _t: ["Animal", "Dog"], ... } { _id: ..., _t: ["Animal", "Cat", "Lion"], ... } { _id: ..., _t: ["Animal", "Cat", "Tiger"], ... }
Importante
Discriminador de clase raíz
Cualquier documento que se asigne a la clase raíz aún utilizará un valor de string para el campo de discriminador.
Al utilizar el HierarchicalDiscriminatorConvention, puede buscar todos los documentos del tipo o subtipo Cat utilizando una única condición booleana, como se muestra en el siguiente ejemplo:
var query = coll.Aggregate().Match(a => a is Cat);
Convenciones personalizadas de discriminador
Si estás trabajando con datos que no siguen las convenciones utilizadas por el controlador .NET/C#; por ejemplo, datos insertados en MongoDB por otro controlador o un mapeador de objetos, es posible que necesites usar un valor diferente para tu campo discriminador para asegurar que tus clases se alineen con esas convenciones.
Si utilizas el automapper, puedes especificar un valor personalizado para el campo discriminador de una clase aplicando el atributo [BsonDiscriminator] a la clase y pasando el valor discriminador personalizado como argumento de string. El siguiente ejemplo de código establece el valor del campo discriminador para la clase Animal en "myAnimalClass":
[] public class Animal { }
Si estás creando un mapa de clases manualmente, llama al método SetDiscriminator() y pasa el valor de discriminador personalizado como argumento al registrar el mapa de clases. El siguiente ejemplo de código establece el valor del campo discriminador para la clase Animal a "myAnimalClass":
BsonClassMap.RegisterClassMap<Animal>(classMap => { classMap.AutoMap(); classMap.SetDiscriminator("myAnimalClass"); });
Una instancia de la anterior clase Animal aparece como sigue tras la serialización:
{ "_id": "...", "_t": "myAnimalClass"}