多态对象
概述
多态对象从一个或多个父类继承属性和方法。这些对象需要特殊的映射以确保.NET/C#驱动程序能够正确地将它们序列化和反序列化为BSON文档。
本指南解释了以下内容
如何反序列化多态类型
包含在.NET/C#驱动程序中的识别符约定
如何创建自定义识别符约定
本页上的示例使用以下继承层次结构
public class Animal { } public class Cat : Animal { } public class Dog : Animal { } public class Lion : Cat { } public class Tiger : Cat { }
反序列化多态对象
在序列化程序可以反序列化任何多态对象之前,您必须记录继承层次结构中所有类的关联关系。
如果您正在使用自动映射器映射您的类,请应用将[BsonKnownTypes]
属性应用到层次结构中的每个基类。将直接从基类继承的每个类作为参数传递。
以下示例展示了如何将[BsonKnownTypes]
属性应用到Animal
层次结构中的类。
[ ]public class Animal { } [ ]public class Cat : Animal { } public class Dog : Animal { } public class Lion : Cat { } public class Tiger : Cat { }
注意
使用BsonKnownTypes
仅将[BsonKnownTypes]
属性应用到父类。仅传递直接从类继承的类型作为参数,而不是层次结构中的所有子类。
如果您正在手动创建类映射,请对层次结构中的每个类调用BsonClassMap.RegisterClassMap<T>()
方法,如下所示。
BsonClassMap.RegisterClassMap<Animal>(); BsonClassMap.RegisterClassMap<Cat>(); BsonClassMap.RegisterClassMap<Dog>(); BsonClassMap.RegisterClassMap<Lion>(); BsonClassMap.RegisterClassMap<Tiger>();
使用区分符
MongoDB中,区分符是添加到文档中的字段,用于标识文档反序列化到的类。当一个集合包含单个继承层次结构中的多个类型时,区分符确保每个文档都反序列化到正确的类。.NET/C#驱动程序将区分符值存储在BSON文档中名为_t
的字段中。通常,_t
是BSON文档中位于_id
之后的第二个字段。
区分符约定定义了区分符字段中存储的值。在本节中,您可以了解包含在.NET/C#驱动程序中的区分符约定以及如何创建自定义区分符约定。
ScalarDiscriminatorConvention
默认情况下,.NET/C#驱动程序使用ScalarDiscriminatorConvention
。根据此约定,.NET/C#驱动程序将_t
字段的值设置为文档序列化时使用的类的名称。
假设您创建了示例Animal
类及其所有子类的实例。如果您将这些对象序列化到单个集合中,.NET/C#驱动程序将应用ScalarDiscriminatorConvention
,相应的BSON文档将如下所示。
{ _id: ..., _t: "Animal", ... } { _id: ..., _t: "Cat", ... } { _id: ..., _t: "Dog", ... } { _id: ..., _t: "Lion", ... } { _id: ..., _t: "Tiger", ... }
ScalarDiscriminatorConvention
使用简洁的区分值,但查询时可能比较困难。例如,要查找所有类型或子类型为Cat
的文档,必须明确列出要查找的每个类。
var query = coll.AsQueryable().Where( item => item.GetType() == typeof(Cat) || item.GetType() == typeof(Lion) || item.GetType() == typeof(Tiger));
注意
OfType()和is运算符
当检查标量区分符的类型时,请使用前面代码示例中显示的Where
语法。如果您尝试使用Aggregate().OfType
方法,或者如果将包含is
运算符的表达式传递给Aggregate().Match()
方法,驱动程序将抛出异常。
分层区分符约定
为了简化对您的多态类型集合的查询,您可以使用HierarchicalDiscriminatorConvention
。根据此约定,_t
的值是文档类型继承层次结构中所有类的数组。
要使用HierarchicalDiscriminatorConvention
,将继承层次结构的基类标记为根类。如果您使用自动映射器,通过将[BsonDiscriminatorAttribute]
属性应用于类并将RootClass = true
作为参数传递来标记根类。以下代码示例将Animal
类标记为例继承层次结构的根。
[ ][ ]public class Animal { }
如果您手动创建类映射,在注册根类的类映射时调用SetIsRootClass()
方法,并将true
作为参数传递。以下代码示例为所有五个示例类注册类映射,但只将Animal
类标记为继承层次结构的根。
BsonClassMap.RegisterClassMap<Animal>(classMap => { classMap.AutoMap(); classMap.SetIsRootClass(true); }); BsonClassMap.RegisterClassMap<Cat>(); BsonClassMap.RegisterClassMap<Dog>(); BsonClassMap.RegisterClassMap<Lion>(); BsonClassMap.RegisterClassMap<Tiger>();
假设您将示例Animal
类标记为继承层次结构的根,然后创建Animal
类及其所有子类的一个实例。将这些对象序列化到单个集合中,.NET/C#驱动程序将应用HierarchicalDiscriminatorConvention
,相应的BSON文档如下所示。
{ _id: ..., _t: "Animal", ... } { _id: ..., _t: ["Animal", "Cat"], ... } { _id: ..., _t: ["Animal", "Dog"], ... } { _id: ..., _t: ["Animal", "Cat", "Lion"], ... } { _id: ..., _t: ["Animal", "Cat", "Tiger"], ... }
重要
根类区分符
映射到根类的任何文档仍然使用字符串值作为区分符字段。
使用HierarchicalDiscriminatorConvention
时,您可以使用单个布尔条件搜索类型或子类型为Cat
的所有文档,如下面的示例所示。
var query = coll.Aggregate().Match(a => a is Cat);
自定义判别器约定
如果您正在处理不遵循.NET/C#驱动程序使用约定的数据(例如,其他驱动程序或对象映射器插入到MongoDB中的数据),您可能需要为判别器字段使用不同的值,以确保您的类与这些约定相匹配。
如果您正在使用自动映射器,可以通过将[BsonDiscriminator]
属性应用于类并将自定义判别器值作为字符串参数传递来指定类判别器字段的值。以下代码示例将Animal
类的判别器字段值设置为"myAnimalClass"。
[ ]public class Animal { }
如果您正在手动创建类映射,请在注册类映射时调用SetDiscriminator()
方法并将自定义判别器值作为参数传递。以下代码示例将Animal
类的判别器字段值设置为"myAnimalClass"。
BsonClassMap.RegisterClassMap<Animal>(classMap => { classMap.AutoMap(); classMap.SetDiscriminator("myAnimalClass"); });
序列化后的上一个Animal
类的实例如下所示
{ "_id": "...", "_t": "myAnimalClass"}