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
- Specify an ID Generator
- Customize DateTime Serialization
- Customize Dictionary Serialization
- Example
- Additional Information
- API Documentation
Overview
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.
Create a POCO
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.
Custom Serialization
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.
Serialize Read-Only Properties
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; } [ ] 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.
Set Field Names
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; } [ ] 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);
Select Type Representation
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 YearBuilt
property, defined as a
char
in C#, is serialized as a BSON Int32
type:
public class House { public Guid Id { get; set; } [ ] 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
.
Set Field Order
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; } [ ] public int YearBuilt { get; set; } [ ] 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.
Identify Id
Property
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 { [ ] 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
.
Omit Empty Fields
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; } [ ] public int YearBuilt { get; set; } public string Style { get; set; } }
Customize Default Values
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; } [ ] 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; } [ ] 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; } [ ] [ ] 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 of1900
.Because
1900
is the default value for this property, the driver will ignore the property if it has this value.
Specify an ID Generator
Every document in a MongoDB collection must have a unique ID. When you serialize an object
to a collection, if the Id
property contains the default
value for its data type, the .NET/C# Driver doesn't serialize it. Instead, the driver
generates a unique ID value for the property.
If the Id
property is of type Guid
, ObjectId
, or string
, the driver can
generate the ID value on its own. If the Id
property is any other data type, you must
specify the IIdGenerator
type that the driver uses to generate the value.
To specify the IIdGenerator
type, apply the [BsonId(IdGenerator)]
attribute
to the property and pass the IIdGenerator
type as an argument.
The .NET/C# Driver includes the following IIdGenerator
types:
Id Field Data Type | IIdGenerator Type |
---|---|
Guid value generated by the COMB algorithm | CombGuidGenerator |
BsonObjectId | BsonObjectIdGenerator |
In the following code example, if the Id
property of the House
class contains
the default value (null
), the driver uses the COMB algorithm to generate a
unique value during serialization:
public class House { [ ] public Guid Id { get; set; } }
Note
In the previous code example, if the Id
property didn't have the
[BsonId(IdGenerator)]
attribute, the driver would generate a non-COMB GUID
to assign to the Id
field.
You can also specify an IIdGenerator
type while registering 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 { [ ] public Guid Id { get; set; } }
Customize DateTime Serialization
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; } [ ] 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; } [ ] 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.
Customize Dictionary Serialization
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 aBsonDocument
. Each entry in the dictionary is aBsonElement
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 validBsonElement
names.ArrayOfArrays: The driver serializes the dictionary to a
BsonArray
. Each entry in the dictionary is a nested, two-elementBsonArray
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 nestedBsonDocument
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 anArrayOfArrays
.
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; } [ ] 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)); });
Example
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 thePrice
field as a BSONDouble
type[BsonDefaultValue()]
, which sets theName
property to"Generic item"
if no value has been assigned to it[BsonDateTimeOptions(DateOnly = true)]
, which specifies that theDateTime
property represents only a date value, with no associated time
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; } }
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\"" } }
Additional Information
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.
API Documentation
To learn more about any of the methods or types discussed in this guide, see the following API documentation: