Docs 菜单
Docs 主页
/ / /
C#/.NET
/ /

POCO

在此页面上

  • Overview
  • 创建 POCO
  • 自定义序列化
  • 序列化只读属性
  • 设置字段名称
  • 选择类型表示
  • 设置字段顺序
  • 识别 Id 属性
  • 省略空字段
  • 自定义默认值
  • 自定义 DateTime 序列化
  • 自定义字典序列化
  • 例子
  • 更多信息
  • API 文档

在本指南中,您可以学习如何使用“Plain Old CLR/Class Objects”(或 POCO),配合 .NET/C# 驱动程序处理您的操作和查询。POCO 是简单的类对象,不继承任何特定于框架的基本类或接口的功能。我们建议在 C# 代码中使用 POCO,以遵循一致的驱动程序用法并实现最佳性能。

请阅读本指南,以了解有关如何将 POCO 与 .NET/C# 驱动程序一起使用的更多信息,或者调整驱动程序的默认字段映射行为。

您可以通过定义不通过框架实现接口或扩展类的简单类来创建 POCO。当您使用 POCO 执行读取或写入等操作时,驱动程序会在内部将 POCO 序列化 或转换为 BSON。

选择 POCOBSON 标签页以查看驱动程序如何将示例 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; }
[BsonElement]
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; }
[BsonElement("year_built")]
public int YearBuilt { get; set; }
}

尽管在定义 C# 类时通常使用 Pascal case 命名规范,但使用 [BsonElement()] 属性允许您在 MongoDB 集合中选择不同的或自定义命名规范。

提示

设置自定义字段名称规范

若要使用自定义字段名称序列化每个属性,您可以定义 ConventionPack 而不是使用 [BsonElement()] 属性。例如,如果您使用 Pascal case 命名规范定义类,则可以使用以下代码在序列化文档中使用大小写字段名称:

var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() };
ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);

要将C#属性序列化为特定的BSON类型,请使用 [BsonRepresentation()] 属性。仅当C#基元类型可转换为您指定的BSON类型时,此操作才有效。

在以下代码示例中,驾驶员将 YearBuilt属性(在C#中定义为 char)序列化为BSON Int32 类型:

public class House
{
public Guid Id { get; set; }
[BsonRepresentation(BsonType.Int32)]
public char YearBuilt { get; set; }
}

有关有效类型转换的更多信息,请参阅 C# 转换规范

重要

序列化 NaN 和 Infinity

如果尝试将浮点 InfinityNaN 值序列化或反序列化为整数表示形式,驱动程序会抛出 OverflowException

驱动程序按照 POCO 中指定的顺序将属性序列化为 BSON 字段。要按照自定义顺序存储属性以匹配现有模式,可以在 [BsonElement()] 属性中指定 Order 命名参数。在以下代码示例中,驱动程序将 YearBuilt 属性存储在 Style 属性之后:

public class House
{
public Guid Id { get; set; }
[BsonElement(Order = 2)]
public int YearBuilt { get; set; }
[BsonElement(Order = 1)]
public string Style { get; set; }
}

如果任何属性没有显式 Order,则驱动程序将在具有显式顺序的属性之后按默认顺序对其进行序列化。

默认情况下,驱动程序会将名为 Idid_id 的任何公共属性映射到 BSON _id 字段。要明确选择映射到 _id 字段的属性,则使用 [BsonId()] 属性。以下代码示例将 Identifier 属性映射到 _id 字段:

public class House
{
[BsonId]
public string Identifier { get; set; }
}

警告

多个ID字段

如果使用 [BsonId()] 属性将多个属性标识为 _id 字段,则驱动程序会抛出 DuplicateBsonMemberMapAttributeException。如果多次指定同一数据库字段(例如,如果 POCO 包含名为 Id_id 的属性),则驱动程序会抛出 BsonSerializationException

注意

嵌套文档 ID

本节中描述的 _id字段映射逻辑仅适用于根文档,应用用于嵌套文档。

MongoDB集合中的每个文档都必须有一个唯一的ID。当您将对象序列化为集合时,如果其ID属性包含其数据类型的默认值(通常为 null) ,.NET/ C#驱动程序不会序列化该默认值。相反,驾驶员会尝试生成唯一的ID值并将其分配给属性。

要为属性启用ID生成,必须指定驾驶员用于该属性的ID生成器。为此,您可以将 [BsonId] 属性应用于属性并传递 IdGenerator 参数以指定生成器类型。下表描述了.NET/ C#驱动程序中可用的ID生成器:

Id 字段数据类型
如何使用

Guid

要使用 COMB算法生成唯一的 Guid 值,应用[BsonId(IdGenerator = typeof(CombGuidGenerator))] 属性应用于ID属性,如以下示例所示:

public class House
{
[BsonId(IdGenerator = typeof(CombGuidGenerator))]
public Guid Id { get; set; }
}

要在不使用 COMB算法的情况下生成唯一的 Guid 值,请不要应用前面的属性应用于ID属性。驾驶员会自动使用 GuidGenerator 类型为ID属性生成唯一值。

public class House
{
public Guid Id { get; set; }
}

ObjectId

对于数据类型为 ObjectId 的ID属性,驾驶员会自动使用 ObjectIdGenerator 类型,如以下代码示例:

public class House
{
public ObjectId Id { get; set; }
}

string

如果指定将 string数据类型的ID属性序列化为 ObjectId,则驾驶员会自动使用 StringObjectIdGenerator 为该属性生成唯一的 string 值。在以下代码示例中,驾驶员将 StringObjectIdGenerator 用于 Id属性:

public class House
{
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
}

要为未序列化为 ObjectId 的ID属性生成唯一的 string 值,应用[BsonID(IdGenerator = typeof(StringObjectIdGenerator))] 属性应用于该属性,如以下代码示例。驾驶员使用 StringObjectIdGenerator 类型生成唯一的 ObjectId 值,将其转换为 string,并将其分配给属性。

public class House
{
[BsonId(IdGenerator = typeof(StringObjectIdGenerator))]
public string Id { get; set; }
}

BsonObjectId

[BsonId(IdGenerator = typeof(BsonObjectIdGenerator))] 属性应用于ID属性,如以下代码示例。驾驶员使用 BsonObjectIdGenerator 类型为属性生成唯一的 BsonObjectId 值。

public class House
{
[BsonId(IdGenerator = typeof(BsonObjectIdGenerator))]
public BsonObjectId Id { get; set; }
}

或者,您可以在注册类映射时指定 IIdGenerator 类型,如以下示例所示:

BsonClassMap.RegisterClassMap<House>(classMap =>
{
classMap.AutoMap();
classMap.MapIdMember(h => h.Id).SetIdGenerator(CombGuidGenerator.Instance);
});

提示

为多个类指定 IIdGenerator

您可以使用 RegisterIdGenerator() 方法为某一数据类型的所有 Id 属性指定一个 IIdGenerator。以下代码示例指示驱动程序对所有 Guid ID 使用 CombGuidGenerator 类型:

BsonSerializer.RegisterIdGenerator(
typeof(Guid),
CombGuidGenerator.Instance
);

.NET/C# 驱动程序还包括 IIdGenerator 类型,用于验证 Id 属性并在 ID 无效时抛出异常。下表列出这些类型:

ID 验证
IIdGenerator 类型

不为空

NullIdChecker

并非全部为零

ZeroIdChecker<T>

在以下代码示例中,如果 House 类的 Id 属性包含默认值 (null),则驱动程序会抛出异常:

public class House
{
[BsonId(IdGenerator = typeof(NullIdChecker))]
public Guid Id { get; set; }
}

默认情况下,驱动程序将未定义的属性序列化为具有 null 值的字段。要在序列化过程中忽略未定义属性,则使用 [BsonIgnore] 属性。以下代码展示了如何在 YearBuilt 属性未定义时阻止驱动程序序列化该属性:

public class House
{
public Guid Id { get; set; }
[BsonIgnore]
public int YearBuilt { get; set; }
public string Style { get; set; }
}

在 C# 中,属性在赋值之前均为默认值。默认值取决于属性的数据类型。例如,引用类型属性的默认值为 null

要为属性指定不同的默认值,则将 [BsonDefaultValue()] 属性应用于该属性并将所需默认值作为参数传递。

以下代码示例将 [BsonDefaultValue()] 属性应用于 YearBuilt 属性。在为该属性分配值之前,其值为 1900

public class House
{
public Guid Id { get; set; }
[BsonDefaultValue(1900)]
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; }
[BsonIgnoreIfDefault]
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; }
[BsonDefaultValue(1900)]
[BsonIgnoreIfDefault]
public int YearBuilt { get; set; }
}

前面的代码示例设置了以下序列化行为:

  • 如果没有为 YearBuilt 属性赋值,则其指定默认值为 1900

  • 由于 1900 是此属性的默认值,因此如果该属性具有此值,则驱动程序将忽略该属性。

要自定义 .NET/C# 驱动程序序列化 DateTime 属性的方式,则使用 [BsonDateTimeOptions()] 属性并将所需设置指定为参数。

如果 DateTime 属性仅表示日期,您可以对其应用 [BsonDateTimeOptions(DateOnly = true)] 属性。如果这样做,驱动程序将不会对该值执行任何时区转换。

在以下代码示例中,PatientRecord 类为 DateOfBirth 属性使用 DateTime[BsonDateTimeOptions(DateOnly = true)] 属性指示该属性仅包含日期。

public class PatientRecord
{
public Guid Id { get; set; }
[BsonDateTimeOptions(DateOnly = true)]
public DateTime DateOfBirth { get; set; }
}

您也可以使用 [BsonDateTimeOptions()] 属性指定 DateTime 属性的 DateTimeKind。在以下代码示例中,PatientRecord 类有一个 AppointmentTime 属性,其类型为 DateTime[BsonDateTimeOptions(Kind = DateTimeKind.Local)] 属性指示属性值的时间部分为当地时间。当驱动程序序列化此属性时,它会将时间转换为 UTC,这是 MongoDB 中存储的时间的标准格式。

public class PatientRecord
{
public Guid Id { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
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));
});

提示

DateTimeKind Values

DateTimeKind 枚举是 .NET Framework 的一部分。有关其节点的更多信息,请参阅 DateTimeKind 枚举的 Microsoft 文档。

DictionaryRepresentation 枚举定义 .NET/C# 驱动程序可用于将 Dictionary 实例进行序列化的格式。该枚举包括以下节点:

  • 文档:(默认)驱动程序将 Dictionary 序列化为 BsonDocument。字典中的每个条目都是一个 BsonElement,其名称等于条目的键,值等于条目的值。仅当字典中的所有键也是有效 BsonElement 名称的字符串时,您才能使用此表示。

  • ArrayOfArrays:驱动程序将字典序列化为 BsonArray。字典中的每个条目都是一个嵌套的二元素 BsonArray,其中包含条目的键和条目的值。

  • ArrayOfDocuments:驱动程序将字典序列化为 BsonArray。字典中的每个条目都是 { k : key, v : value } 形式的嵌套 BsonDocument。由于键和值都用元素名称标记,因此您能够比 ArrayOfArrays 更直观地查询此格式。

在以下代码示例中,RoomSizes 属性是字典,其中包含房屋中的每个房间及其相应的大小。[BsonDictionaryOptions()] 属性指示 .NET/C# 驱动程序将此属性序列化为 BsonArray 对象,并将字典中的每个条目序列化为 { k : "<room>", v : <size> } 形式的 BsonDocument

public class House
{
public Guid Id { get; set; }
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
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 字段序列化为 BSON Double 类型

  • [BsonDefaultValue()],如果尚未为 Name 属性分配任何值,则将其设置为 "Generic item"

  • [BsonDateTimeOptions(DateOnly = true)],指定 DateTime 属性仅代表一个日期值,而没有关联的时间

public class Clothing
{
public ObjectId Id { get; set; }
[BsonElement("name")]
[BsonDefaultValue("Generic item")]
public string Name { get; set; }
[BsonElement("inStock")]
public bool InStock { get; set; }
[BsonElement("price")]
[BsonRepresentation(BsonType.Decimal128)]
public decimal Price { get; set; }
[BsonElement("colorSelection")]
public List<string> ColorSelection { get; set; }
[BsonElement("listedDate")]
[BsonDateTimeOptions(DateOnly = true)]
public DateTime ListedDate { get; set; }
[BsonElement("sizeGuide")]
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\""
}
}

有关序列化相关属性的完整列表,请参阅 Serialization.Attributes API 文档

有关使用 POCOS 的其他读写操作示例,请参阅 使用示例CRUD 基础知识页面

要了解有关驱动程序如何将 BSON 文档映射到 POCO 的更多信息,请参阅类映射

要进一步了解本指南所讨论的任何方法或类型,请参阅以下 API 文档:

后退

类映射