多态对象
Overview
多态对象从一个或多个父类继承属性和方法。这些对象需要特殊的映射,以确保 .NET/C# 驱动程序能正确地将其序列化到 BSON 文档或从 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 { }
注意
Using BsonKnownTypes
仅将 [BsonKnownTypes]
属性应用于父类。只传递直接继承自类的类型作为参数,而非层次结构中的所有子类。
如果手动创建类映射,请为层次结构中的每个类调用 BsonClassMap.RegisterClassMap<T>()
方法,如下例所示:
BsonClassMap.RegisterClassMap<Animal>(); BsonClassMap.RegisterClassMap<Cat>(); BsonClassMap.RegisterClassMap<Dog>(); BsonClassMap.RegisterClassMap<Lion>(); BsonClassMap.RegisterClassMap<Tiger>();
使用鉴别器
在 MongoDB 中,鉴别器是添加到文档中的字段,用于标识文档反序列化到的类。 当collection包含来自单个继承层次结构的多个类型时,鉴别器可确保每个文档反序列化为正确的类。.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<T>() 和 is 操作符
检查标量鉴别器的类型时,请使用前面的代码示例所示的 Where
语法。如果您尝试使用 Aggregate().OfType<T>()
方法,或者将包含 is
操作符的表达式传递给 Aggregate().Match()
方法,驾驶员会引发异常。
HierarchicalDiscriminatorConvention
要简化对多态类型集合的查询,您可以使用 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"}