Docs Menu
Docs Home
/ / /
C#/.NET
/ /

POCOs

On this page

  • Overview
  • Create a POCO
  • Custom Serialization
  • Serialize Read-Only Properties
  • Set Field Names
  • Select Type Representation
  • Set Field Order
  • Identify Id Property
  • Omit Empty Fields
  • Customize Default Values
  • Customize DateTime Serialization
  • Customize Dictionary Serialization
  • Example
  • Additional Information
  • API Documentation

In this guide, you can learn about how you can use "Plain Old CLR/Class Objects", or POCOs, with the .NET/C# Driver for your operations and queries. POCOs are simple class objects that do not inherit features from any framework-specific base classes or interfaces. We recommend using POCOs in your C# code to adhere to idiomatic driver usage and achieve the best performance.

You should read this guide if you want to learn more about how to use POCOs with the .NET/C# Driver or if you need to adjust the driver's default field mapping behavior.

You can create a POCO by defining a simple class that does not implement interfaces or extend classes from a framework. When you execute an operation such as a read or write using a POCO, the driver internally serializes, or converts, the POCO to BSON.

Select the POCO or BSON tab to see how the driver serializes a sample POCO to 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" ]
}

You can define a POCO with any object structure that suits your needs, including nested objects, arrays, lists, and any data types.

If the default field mapping behavior does not meet your needs, you can specify custom behavior using serialization-related attributes. These attributes change the way that the driver serializes each property of your POCO. This section describes some of the common serialization-related attributes.

If a property is read-only, the automapper doesn't include it in the class map for serialization. To force the automapper to include a property in the class map, apply the [BsonElement] attribute to the property.

The following code example applies the [BsonElement] attribute to the Upc property of the Clothing class. Upc is a read-only property because it has a get method but no set method.

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; }
}

You can also add a read-only property when you register the class map, as shown in the following example:

BsonClassMap.RegisterClassMap<Clothing>(classMap =>
{
classMap.AutoMap();
classMap.MapProperty(c => c.Upc);
});

Note

When the .NET/C# Driver serializes a read-only property, the property and its value are stored in the database, but never deserialized again.

The driver serializes POCO properties to BSON fields with the same field name and capitalization. To store a property under a different name, use the [BsonElement()] attribute. The following code maps the YearBuilt property of the House class to the year_built field in the serialized BSON document:

public class House
{
public Guid Id { get; set; }
[BsonElement("year_built")]
public int YearBuilt { get; set; }
}

Though it is common to use the Pascal case naming convention when defining C# classes, using the [BsonElement()] attribute allows you to select a different or custom naming convention in your MongoDB collection.

Tip

Set Custom Field Name Convention

If you want to serialize every property with a custom field name, you can define a ConventionPack instead of using the [BsonElement()] attribute. For example, if you define your class using the Pascal case naming convention, you can use the following code to use camel case field names in the serialized document:

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

To serialize a C# property to a specific BSON type, use the [BsonRepresentation()] attribute. This works only if the C# primitive type is convertible to the BSON type you specify.

In the following code sample, the driver serializes the YearBuilt property, defined as a char in C#, as a BSON Int32 type:

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

For more information on valid type conversions, see the C# Conversions Specification.

Important

Serializing NaN and Infinity

If you try to serialize or deserialize a floating-point Infinity or NaN value to an integral representation, the driver throws an OverflowException.

The driver serializes properties to BSON fields in the order they are specified in the POCO. To store properties in a custom order to match an existing schema, you can specify the Order named parameter in the [BsonElement()] attribute. In the following code sample, the driver stores the YearBuilt property after the Style property:

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

If any properties don't have an explicit Order, the driver will serialize them in the default order after those that do.

By default, the driver maps any public property named Id, id, or _id to the BSON _id field. To explicitly select the property to map to the _id field, use the [BsonId()] attribute. The following code sample maps the Identifier property to the _id field:

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

Warning

Multiple ID Fields

If you identify more than one property as the _id field using the [BsonId()] attribute, the driver throws a DuplicateBsonMemberMapAttributeException. If you specify the same database field more than once (for example, if your POCO includes properties named Id and _id), the driver throws a BsonSerializationException.

Note

Nested Document Ids

The _id field mapping logic described in this section only applies to the root document and does not apply to nested documents.

Every document in a MongoDB collection must have a unique ID. When you serialize an object to a collection, if its ID property contains the default value for its data type (usually null), the .NET/C# Driver doesn't serialize the default value. Instead, the driver tries to generate a unique ID value and assign it to the property.

To enable ID generation for a property, you must specify the ID generator the driver uses for the property. You can do so by applying the [BsonId] attribute to the property and passing the IdGenerator argument to specify the generator type. The following table describes the ID generators available in the .NET/C# Driver:

Id Field Data Type
How to Use

Guid

To use the COMB algorithm to generate a unique Guid value, apply the [BsonId(IdGenerator = typeof(CombGuidGenerator))] attribute to the ID property, as shown in the following example:

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

To generate a unique Guid value without the COMB algorithm, don't apply the preceding attribute to the ID property. The driver automatically uses the GuidGenerator type to generate a unique value for the ID property.

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

ObjectId

The driver automatically uses the ObjectIdGenerator type for ID properties with the ObjectId data type, such as the one in the following code example:

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

string

If you specify that an ID property with the string data type is serialized as an ObjectId, the driver automatically uses the StringObjectIdGenerator to generate a unique string value for the property. In the following code example, the driver uses the StringObjectIdGenerator for the Id property:

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

To generate a unique string value for an ID property that is not serialized as an ObjectId, apply the [BsonID(IdGenerator = typeof(StringObjectIdGenerator))] attribute to the property, as shown in the following code example. The driver uses the StringObjectIdGenerator type to generate a unique ObjectId value, convert it to a string, and assign it to the property.

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

BsonObjectId

Apply the [BsonId(IdGenerator = typeof(BsonObjectIdGenerator))] attribute to the ID property, as shown in the following code example. The driver uses the BsonObjectIdGenerator type to generate a unique BsonObjectId value for the property.

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

Alternatively, you can specify an IIdGenerator type when you register the class map, as shown in the following example:

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

Tip

Specify an IIdGenerator for Multiple Classes

You can use the RegisterIdGenerator() method to specify a single IIdGenerator for all Id properties of a certain data type. The following code example instructs the driver to use the CombGuidGenerator type for all Guid IDs:

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

The .NET/C# Driver also includes IIdGenerator types that validate the Id property and throw an exception if the ID is invalid. The following table lists these types:

ID Validation
IIdGenerator Type

Not null

NullIdChecker

Not all zeroes

ZeroIdChecker<T>

In the following code example, if the Id property of the House class contains the default value (null), the driver throws an exception:

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

By default, the driver serializes undefined properties to fields with null values. To ignore undefined properties during serialization, use the [BsonIgnore] attribute. The following code shows how you can prevent the driver from serializing the YearBuilt property if it is undefined:

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

In C#, a property has a default value until you assign a value to it. The default value depends on the property's data type. For example, the default value for a reference-type property is null.

To specify a different default value for a property, apply the [BsonDefaultValue()] attribute to the property and pass the desired default value as an argument.

The following code examples applies the [BsonDefaultValue()] attribute to the YearBuilt property. Until this property is assigned a value, its value is 1900.

public class House
{
public Guid Id { get; set; }
[BsonDefaultValue(1900)]
public int YearBuilt { get; set; }
}

You can also specify a different default value for a property when you register the class map, as shown in the following example:

BsonClassMap.RegisterClassMap<House>(classMap =>
{
classMap.AutoMap();
classMap.MapMember(h => h.YearBuilt).SetDefaultValue(1900);
});

By default, the .NET/C# Driver serializes all properties, including those that contain default values. To instruct the driver to ignore a property that has the default value, use the [BsonIgnoreIfDefault] attribute.

The following code example applies the [BsonIgnoreIfDefault] attribute to the YearBuilt property. If the value of this property is the default for its data type (0 for int properties), the driver won't serialize it.

public class House
{
public Guid Id { get; set; }
[BsonIgnoreIfDefault]
public int YearBuilt { get; set; }
}

You can also instruct the driver to ignore a property that contains the default value when you register the class map, as shown in the following example:

BsonClassMap.RegisterClassMap<House>(classMap =>
{
classMap.AutoMap();
classMap.MapMember(h => h.YearBuilt).SetIgnoreIfDefault(true);
});

You can both specify a different default value for a property and instruct the driver to ignore the property if it contains this default value. To do so, apply both the [BsonDefaultValue()] and [BsonIgnoreIfDefault] attributes to the property, as shown in the following code example:

public class House
{
public Guid Id { get; set; }
[BsonDefaultValue(1900)]
[BsonIgnoreIfDefault]
public int YearBuilt { get; set; }
}

The previous code example sets the following serialization behavior:

  • If a value hasn't been assigned to the YearBuilt property, it has the specified default value of 1900.

  • Because 1900 is the default value for this property, the driver will ignore the property if it has this value.

To customize how the .NET/C# Driver serializes DateTime properties, use the [BsonDateTimeOptions()] attribute and specify the desired setting as an argument.

If a DateTime property represents only a date, you can apply the [BsonDateTimeOptions(DateOnly = true)] attribute to it. If you do, the driver won't perform any time-zone conversion on the value.

In the following code example, the PatientRecord class uses a DateTime for the DateOfBirth property. The [BsonDateTimeOptions(DateOnly = true)] attribute indicates that the property contains only a date.

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

You can also use the [BsonDateTimeOptions()] attribute to specify the DateTimeKind of a DateTime property. In the following code example, the PatientRecord class has an AppointmentTime property of type DateTime. The [BsonDateTimeOptions(Kind = DateTimeKind.Local)] attribute indicates that the time component of the property's value is in local time. When the driver serializes this property, it converts the time to UTC, the standard format for times stored in MongoDB.

public class PatientRecord
{
public Guid Id { get; set; }
[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
public DateTime AppointmentTime { get; set; }
}

You can also specify one or both of the previous DateTime options when registering the class map:

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));
});

Tip

DateTimeKind Values

The DateTimeKind enum is part of the .NET framework. For more information on its members, see the Microsoft documentation for the DateTimeKind enum.

The DictionaryRepresentation enum defines the formats the .NET/C# Driver can serialize a Dictionary instance to. This enum includes the following members:

  • Document: (Default) The driver serializes the Dictionary to a BsonDocument. Each entry in the dictionary is a BsonElement with a name equal to the entry's key and a value equal to the entry's value. You can use this representation only when all the keys in the dictionary are strings that are also valid BsonElement names.

  • ArrayOfArrays: The driver serializes the dictionary to a BsonArray. Each entry in the dictionary is a nested, two-element BsonArray that contains the entry's key and the entry's value.

  • ArrayOfDocuments: The driver serializes the dictionary to a BsonArray. Each entry in the dictionary is a nested BsonDocument of the form { k : key, v : value }. Because the keys and values are tagged with element names, you can query this format more intuitively than an ArrayOfArrays.

In the following code example, the RoomSizes property is a dictionary that contains each room in a house and its corresponding size. The [BsonDictionaryOptions()] attribute instructs the .NET/C# Driver to serialize this property to a BsonArray object, and each entry in the dictionary to a BsonDocument of the form { k : "<room>", v : <size> }.

public class House
{
public Guid Id { get; set; }
[BsonDictionaryOptions(DictionaryRepresentation.ArrayOfDocuments)]
public Dictionary<string, float> RoomSizes { get; set; }
}

You can also specify the serialization format of a dictionary when you register the class map, as shown in the following example:

BsonClassMap.RegisterClassMap<House>(classMap =>
{
classMap.AutoMap();
classMAp.MapMember(h => h.RoomSizes)
.SetSerializer(new DictionaryInterfaceImplementerSerializer<Dictionary<string, float>>
(DictionaryRepresentation.ArrayOfDocuments));
});

The following example shows how to insert a Clothing document with custom field mapping specifications into MongoDB.

The following code defines the Clothing class with these serialization-related attributes:

  • [BsonElement()], which specifies custom field names in the camel case naming convention

  • [BsonRepresentation()], which specifies serialization of the Price field as a BSON Double type

  • [BsonDefaultValue()], which sets the Name property to "Generic item" if no value has been assigned to it

  • [BsonDateTimeOptions(DateOnly = true)], which specifies that the DateTime property represents only a date value, with no associated time

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; }
}

The following code instantiates a Clothing object and inserts the document into a collection:

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);

The BSON representation of the inserted document looks like this:

{
"_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\""
}
}

For a full list of serialization-related attributes, see the Serialization.Attributes API documentation.

For additional read and write operation examples using POCOs, see the Usage Examples or the CRUD Fundamentals Pages.

To learn more about how the driver maps BSON documents to POCOs, see Class Mapping.

To learn more about any of the methods or types discussed in this guide, see the following API documentation:

Back

Class Mapping