POCOs
概述
在本指南中,您可以了解如何使用 "普通的CLR/类对象",或称 POCOs,配合.NET/C#驱动进行操作和查询。POCOs是简单的类对象,不继承任何框架特定的基类或接口。我们建议在C#代码中使用POCOs,以符合惯用驱动使用并实现最佳性能。
如果您想了解更多关于如何使用POCOs与.NET/C#驱动或需要调整驱动默认字段映射行为的信息,请阅读本指南。
创建一个POCO
您可以通过定义一个不实现接口或不从框架类扩展的简单类来创建一个POCO。当您使用POCO执行读取或写入等操作时,驱动程序会内部 序列化,或转换,POCO为BSON。
选择POCO 或 BSON 选项卡,查看驱动程序如何序列化一个示例POCO为BSON
public class Clothing { public ObjectId Id { get; set; } public string Name { get; set; } public bool InStock { get; set; } public double Price { get; set; } public List<string> ColorSelection { get; set; } }
{ "_id": ObjectId("..."), "Name": "Long Sleeve Shirt", "InStock": true, "Price": 17.99, "ColorSelection": [ "black", "navy", "red" ] }
您可以根据需要定义任何对象结构来创建POCO,包括嵌套对象、数组、列表和任何数据类型。
自定义序列化
如果默认字段映射行为不能满足您的需求,您可以使用与序列化相关的属性来指定自定义行为。这些属性会改变驱动程序序列化 POCO 每个属性的方式。本节描述了一些常见的序列化相关属性。
序列化只读属性
如果属性是只读的,自动映射器不会将其包含在类映射中用于序列化。要强制自动映射器将属性包含在类映射中,将[BsonElement] 属性应用于属性。
以下代码示例将 [BsonElement] 属性应用于 Clothing 类的 Upc 属性。 Upc 是一个只读属性,因为它有一个 get 方法但没有 set 方法。
public class Clothing { public ObjectId Id { get; set; } public string Name { get; set; } public bool InStock { get; set; } public double Price { get; set; } public List<string> ColorSelection { get; set; } [] public int Upc { get; } }
您也可以在注册类映射时添加只读属性,如下例所示
BsonClassMap.RegisterClassMap<Clothing>(classMap => { classMap.AutoMap(); classMap.MapProperty(c => c.Upc); });
注意
当 .NET/C# 驱动程序序列化只读属性时,属性及其值将存储在数据库中,但不会再次反序列化。
设置字段名称
驱动程序将POCO属性序列化为具有相同字段名称和大写的BSON字段。要使用不同的名称存储属性,请使用[BsonElement()]属性。以下代码将House类中的YearBuilt属性映射到序列化BSON文档中的year_built字段。
public class House { public Guid Id { get; set; } [] public int YearBuilt { get; set; } }
尽管在定义C#类时使用Pascal命名约定很常见,但使用[BsonElement()]属性允许您在MongoDB集合中选择不同的或自定义的命名约定。
提示
设置自定义字段名称约定
如果您想将每个属性序列化为自定义字段名称,则可以定义一个ConventionPack而不是使用[BsonElement()]属性。例如,如果您使用Pascal命名约定定义类,则可以使用以下代码在序列化文档中使用camel case字段名称
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() }; ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);
选择类型表示
要将C#属性序列化到特定的BSON类型,请使用[BsonRepresentation()]属性。这仅在C#原始类型可以转换为指定的BSON类型时才有效。在以下代码示例中,将定义为C#中char的YearBuilt属性序列化为BSON Int32类型。
public class House { public Guid Id { get; set; } [] public char YearBuilt { get; set; } }
有关有效类型转换的更多信息,请参阅C#转换规范。
重要
序列化NaN和Infinity
如果您尝试将浮点数Infinity或NaN值序列化为整型表示,则驱动程序会抛出OverflowException。
设置字段顺序
驱动程序将属性序列化为BSON字段,其顺序与POCO中指定的顺序一致。为了将属性按自定义顺序存储以匹配现有模式,您可以在[BsonElement()]属性中指定名为Order的命名参数。在以下代码示例中,驱动程序将YearBuilt属性存储在Style属性之后
public class House { public Guid Id { get; set; } [] public int YearBuilt { get; set; } [] public string Style { get; set; } }
如果任何属性没有明确的Order,则驱动程序将它们按默认顺序序列化,在这些有Order的属性之后。
识别Id属性
默认情况下,驱动程序将任何名为Id、id或_id的公共属性映射到BSON的_id字段。要显式选择映射到_id字段的属性,请使用[BsonId()]属性。以下代码示例将Identifier属性映射到_id字段
public class House { [] public string Identifier { get; set; } }
警告
多个Id字段
如果您使用[BsonId()]属性将多个属性识别为_id字段,驱动程序将抛出DuplicateBsonMemberMapAttributeException异常。如果您指定了同一数据库字段多次(例如,如果您的POCO包含名为Id和_id的属性),驱动程序将抛出BsonSerializationException异常。
注意
嵌套文档的_id
本节中描述的_id字段映射逻辑仅适用于根文档,不适用于嵌套文档。
省略空字段
默认情况下,驱动程序将未定义属性序列化为具有 null 值的字段。要忽略序列化过程中的未定义属性,请使用 [BsonIgnore] 属性。以下代码演示了如何防止驱动程序序列化未定义的 YearBuilt 属性。
public class House { public Guid Id { get; set; } [] public int YearBuilt { get; set; } public string Style { get; set; } }
自定义默认值
在C#中,属性在您为其分配值之前具有默认值。默认值取决于属性的数据类型。例如,引用类型属性的默认值是 null。
要为属性指定不同的默认值,请将 [BsonDefaultValue()] 属性应用于该属性,并将所需的默认值作为参数传递。
以下代码示例将 [BsonDefaultValue()] 属性应用于 YearBuilt 属性。直到为此属性分配值,其值始终为 1900。
public class House { public Guid Id { get; set; } [] public int YearBuilt { get; set; } }
您还可以在注册类图时为属性指定不同的默认值,如下例所示。
BsonClassMap.RegisterClassMap<House>(classMap => { classMap.AutoMap(); classMap.MapMember(h => h.YearBuilt).SetDefaultValue(1900); });
默认情况下,.NET/C# 驱动程序序列化所有属性,包括包含默认值的属性。要指示驱动程序忽略具有默认值的属性,请使用 [BsonIgnoreIfDefault] 属性。
以下代码示例将[BsonIgnoreIfDefault]属性应用于YearBuilt属性。如果该属性的值是其数据类型的默认值(对于int属性为0),则驱动程序不会对其进行序列化。
public class House { public Guid Id { get; set; } [] public int YearBuilt { get; set; } }
您还可以在注册类映射时指示驱动程序忽略包含默认值的属性,如下例所示
BsonClassMap.RegisterClassMap<House>(classMap => { classMap.AutoMap(); classMap.MapMember(h => h.YearBuilt).SetIgnoreIfDefault(true); });
您可以为属性指定不同的默认值,并指示驱动程序在属性包含此默认值时忽略它。为此,将[BsonDefaultValue()]和[BsonIgnoreIfDefault]属性应用于该属性,如下例所示
public class House { public Guid Id { get; set; } [] [] public int YearBuilt { get; set; } }
前面的代码示例设置了以下序列化行为
如果尚未为
YearBuilt属性分配值,它具有指定的默认值1900。因为
1900是该属性的默认值,所以如果属性具有此值,驱动程序将忽略它。
指定ID生成器
在MongoDB集合中,每个文档都必须有一个唯一的ID。当您将对象序列化到集合中时,如果Id属性包含其数据类型的默认值,.NET/C#驱动程序不会对其进行序列化。相反,驱动程序会为该属性生成一个唯一的ID值。
如果Id属性是Guid、ObjectId或string类型,驱动程序可以自行生成ID值。如果Id属性是其他数据类型,您必须指定驱动程序用于生成值的IIdGenerator类型。要指定IIdGenerator类型,将[BsonId(IdGenerator)]属性应用于该属性,并将IIdGenerator类型作为参数传递。
.NET/C#驱动程序包含以下IIdGenerator类型
Id字段数据类型 | IIdGenerator类型 |
|---|---|
由COMB算法生成的 |
|
|
|
在以下代码示例中,如果House类的Id属性包含默认值(null),则驱动程序在序列化期间使用COMB算法生成一个唯一的值
public class House { [] public Guid Id { get; set; } }
注意
在先前的代码示例中,如果Id属性没有[BsonId(IdGenerator)]属性,驱动程序将生成一个非COMB的GUID分配给Id字段。
您还可以在注册类映射时指定IIdGenerator类型,如下例所示
BsonClassMap.RegisterClassMap<House>(classMap => { classMap.AutoMap(); classMap.MapIdMember(h => h.Id).SetIdGenerator(CombGuidGenerator.Instance); });
提示
为多个类指定IIdGenerator
您可以使用RegisterIdGenerator()方法为特定数据类型的所有Id属性指定单个IIdGenerator。以下代码示例指导驱动程序使用CombGuidGenerator类型对所有Guid ID进行操作。
BsonSerializer.RegisterIdGenerator( typeof(Guid), CombGuidGenerator.Instance );
.NET/C#驱动程序还包括验证Id属性并在ID无效时抛出异常的IIdGenerator类型。以下表格列出了这些类型。
ID验证 | IIdGenerator类型 |
|---|---|
非空 |
|
非全零 |
|
在以下代码示例中,如果House类的Id属性包含默认值(null),则驱动程序会抛出异常。
public class House { [] public Guid Id { get; set; } }
自定义DateTime序列化
要自定义.NET/C#驱动程序如何序列化DateTime属性,请使用[BsonDateTimeOptions()]属性并将所需的设置作为参数指定。
如果DateTime属性仅表示日期,您可以将其应用于[BsonDateTimeOptions(DateOnly = true)]属性。如果您这样做,则驱动程序不会对值执行任何时区转换。
在以下代码示例中,PatientRecord类使用DateTime作为DateOfBirth属性。[BsonDateTimeOptions(DateOnly = true)]属性表示该属性仅包含日期。
public class PatientRecord { public Guid Id { get; set; } [] public DateTime DateOfBirth { get; set; } }
您还可以使用[BsonDateTimeOptions()]属性来指定DateTime属性的DateTimeKind。在以下代码示例中,PatientRecord类具有一个类型为DateTime的AppointmentTime属性。[BsonDateTimeOptions(Kind = DateTimeKind.Local)]属性表示属性值的时间组件为本地时间。当驱动程序序列化此属性时,它会将时间转换为UTC,这是存储在MongoDB中的时间标准格式。
public class PatientRecord { public Guid Id { get; set; } [] public DateTime AppointmentTime { get; set; } }
在注册类映射时,您也可以指定一个或两个之前的DateTime选项。
BsonClassMap.RegisterClassMap<House>(classMap => { classMap.AutoMap(); classMap.MapMember(p => p.DateOfBirth) .SetSerializer(new DateTimeSerializer(dateOnly: true)); classMap.MapMember(p => p.AppointmentTime) .SetSerializer(new DateTimeSerializer(DateTimeKind.Local)); });
自定义字典序列化
DictionaryRepresentation 枚举定义了 .NET/C# 驱动程序可以将 Dictionary 实例序列化为的格式。此枚举包括以下成员
Document:(默认)驱动程序将
Dictionary序列化为一个BsonDocument。字典中的每个条目都是一个具有与条目键相同的名称和与条目值相等的值的BsonElement。您只能使用此表示法,当字典中的所有键都是也是有效的BsonElement名称的字符串时。ArrayOfArrays:驱动程序将字典序列化为一个
BsonArray。字典中的每个条目都是一个包含条目的键和值的嵌套、两个元素的BsonArray。ArrayOfDocuments:驱动程序将字典序列化为一个
BsonArray。字典中的每个条目都是一个形式为{ k : key, v : value }的嵌套BsonDocument。由于键和值都带有元素名称标签,因此您可以比ArrayOfArrays格式更直观地查询此格式。
以下代码示例中,RoomSizes 属性是一个字典,包含房屋中的每个房间及其对应的大小。[BsonDictionaryOptions()] 属性指示 .NET/C# 驱动程序将此属性序列化为 BsonArray 对象,并将字典中的每个条目序列化为具有以下形式的 BsonDocument 对象:{ k : "<room>", v : <size> }。
public class House { public Guid Id { get; set; } [] public Dictionary<string, float> RoomSizes { get; set; } }
您还可以在注册类映射时指定字典的序列化格式,如下例所示
BsonClassMap.RegisterClassMap<House>(classMap => { classMap.AutoMap(); classMAp.MapMember(h => h.RoomSizes) .SetSerializer(new DictionaryInterfaceImplementerSerializer<Dictionary<string, float>> (DictionaryRepresentation.ArrayOfDocuments)); });
示例
以下示例演示了如何将具有自定义字段映射规范的 Clothing 文档插入到 MongoDB 中。
以下代码定义了具有以下序列化相关属性的 Clothing 类
[BsonElement()],指定了驼峰命名法的自定义字段名称[BsonRepresentation()],指定将Price字段序列化为 BSONDouble类型[BsonDefaultValue()],如果未为其分配值,则将Name属性设置为"Generic item"[BsonDateTimeOptions(DateOnly = true)],指定DateTime属性仅表示日期值,没有相关的时间
public class Clothing { public ObjectId Id { get; set; } [] [] public string Name { get; set; } [] public bool InStock { get; set; } [] [] public decimal Price { get; set; } [] public List<string> ColorSelection { get; set; } [] [] public DateTime ListedDate { get; set; } [] public Dictionary<string, string> SizeGuide { get; set; } }
以下代码创建了一个 Clothing 对象并将其插入到集合中
var doc = new Clothing { Name = "Denim Jacket", InStock = false, Price = 32.99m, ColorSelection = new List<string> { "dark wash", "light wash" }, ListedDate = DateTime.Parse("Jan 1, 2007"), SizeGuide = new Dictionary<string, string>() { {"Small", "Chest: 38\", Waist: 38\", Shoulders: 15\""}, {"Medium", "Chest: 40\", Waist: 40\", Shoulders: 15.5\""}, {"Large", "Chest: 42\", Waist: 40\", Shoulders: 16\""} } }; _myColl.InsertOne(doc);
插入文档的 BSON 表示形式如下所示
{ "_id": ObjectId("..."), "name": "Denim Jacket", "inStock": false, "price": 32.99, "colorSelection": [ "dark wash", "light wash" ], "listedDate" : ISODate("2007-01-01T00:00:00Z"), "sizeGuide" : { "Small" : "Chest: 38\", Waist: 38\", Shoulders: 15\"", "Medium" : "Chest: 40\", Waist: 40\", Shoulders: 15.5\"", "Large" : "Chest: 42\", Waist: 40\", Shoulders: 16\"" } }
附加信息
有关序列化相关属性的完整列表,请参阅序列化属性API文档。
有关使用POCO的附加读写操作示例,请参阅使用示例 或 CRUD基础知识页面。
要了解更多关于驱动程序如何将BSON文档映射到POCO的信息,请参阅 类映射。
API文档
要了解更多关于本指南中讨论的任何方法或类型,请参阅以下API文档