MongoDB in .NET part 3: starting with POCO documents

Introduction

In the previous post we set up our MongoDb server and saw how to establish a connection to it. We’ll now start inserting objects into the database. In other words we’ll create MongoDb documents.

Note that there are basically two ways to create documents: either via low-level BSON objects or your “proper” custom POCO ones that you create in your domain layer. In this tutorial we’ll not cover BSON documents. I think most developers prefer the more strongly typed POCO way of working with documents. With BSON you have to declare your object structure through strings.

Here’s a code example of creating a BSON document in C# from the C# driver tutorial:

BsonDocument document = new BsonDocument 
{
    { "name", name },
    { "city", city }, // not added if city is null
    { "dob", dob, dobAvailable } // not added if dobAvailable is false
};

Another example:

BsonDocument nested = new BsonDocument {
{ "name", "John Doe" },
    { "address", new BsonDocument {
        { "street", "123 Main St." },
        { "city", "Centerville" },
        { "state", "PA" },
        { "zip", 12345}
    }}
};

We’ll only look at POCOs here. I think you’ll agree that it’s easier to work with “real” objects for all CRUD operations and queries.

Data serialisation

The C# driver will translate to and from BSON and POCO automatically through data (de)serialisation. The default serialisation is very simple. Say we have the following POCO:

public class Customer
{
	public string Name { get; set; }
	public string Address { get; set; }
}

The default JSON representation of this POCO will be:

{
    "Name" : "Elvis"
    , "Address" : "Graceland"
}

The POCO will be saved in the BSON format but the above JSON helps visualise the binary source. If the POCO has some sequence of objects, e.g.:

public class Customer
{
	public string Name { get; set; }
	public string Address { get; set; }
        IEnumerable<string> Telephones { get; set; }
}

The it’s translated into a JSON array:

{
    "Name" : "Elvis"
    , "Address" : "Neverland"
    , "Telephones" : ["123", "456"]
}

Nested objects such as…

public class Customer
{
	public string Name { get; set; }
	public string Address { get; set; }
	IEnumerable<string> Telephones { get; set; }
	public WebPage PublicPage { get; set; }
}

public class WebPage
{
	public bool IsSsl { get; set; }
	public string Domain { get; set; }
}

…are represented as nested documents like here:

{
    "Name" : "Elvis"
    , "Address" : "Neverland"
    , "Telephones" : ["123", "456"]
    , "PublicPage" : { "IsSsl": true, "Domain" : "company.com" }
}

I think you’re beginning to see the modelling advantage of MongoDb compared to RMDBS databases. You can have any kind of object structure, the resulting document will immediately store it in the appropriate JSON/BSON format without any extra mapping, schema changes, data migrations etc. Feel free to describe your business model in POCOs with no constraints laid by the database. Have nested objects, lists, arrays etc. inside your domain objects – MongoDb documents will be able to handle them with no extra effort.

Changing the default serialisation

Occasionally you may want to deviate from the standard serialisation mechanism: you can ignore certain properties or define a different property name. Serialisation attributes in MongoDb will help you a great deal here. They are similar to the DataMember, IgnoreMember etc. attributes in .NET serialisation. Examples:

To ignore a property use BsonIgnore:

public class Customer
{
	public string Name { get; set; }
	public string Address { get; set; }
	IEnumerable<string> Telephones { get; set; }
	[BsonIgnore]
	public WebPage PublicPage { get; set; }
}

The PublicPage property will not be serialised in the BSON document.

To store a property under a different name use the BsonElement attribute:

public class Customer
{		
	public string Name { get; set; }
	public string Address { get; set; }
	IEnumerable<string> Telephones { get; set; }
	[BsonElement("PublicWebPage")]
	public WebPage PublicPage { get; set; }
}

You might want to do this if the Customer object had a property called PublicWebPage before but was renamed to PublicPage. You still want all the old records to be deserialised so you can declare the name of the property like that.

Ignore NULL values:

public class Customer
{		
	public string Name { get; set; }
	[BsonIgnoreIfNull]
	public string Address { get; set; }
}

By default private fields are not serialised. You can use the empty BsonElement to include it in the serialisation:

public class Customer
{		
	public string Name { get; set; }
	public string Address { get; set; }
	IEnumerable<string> Telephones { get; set; }
	[BsonElement]
	private WebPage PublicPage { get; set; }
}

Declare how the property will be represented in BSON, i.e. under which type, using the BsonRepresentation attribute:

public class Customer
{
	[BsonRepresentation(MongoDB.Bson.BsonType.Double)]
	public decimal TotalOrders { get; set; }
}

The .NET decimal type has no equivalent in BSON, it’s treated as a string which makes sorting and comparing inefficient. It’s better to cast it to a BSON double which is close enough.

We saw that the unique ID field is called “_id” by default and is of type ObjectId. You can declare the ID field using the BsonId attribute:

public class Customer
{
	[BsonId]
	public int CustomerId { get; set; }
}

The ID will still be serialised as “_id” in the document but you can refer to it as “CustomerId” in the POCO. Keep in mind, that it’s your responsibility to create unique integer IDs – there’s no equivalent of SQL Server’s auto-increment (1,1) in MongoDb. In case you stick to the ObjectId type then MongoDb will create unique values for you.

Dates are always a different beast with all the time zones and formats. MongoDb translates all local dates to UTC automatically when a date field is serialised. However, it can create problems when the date is deserialised. You’ll by default get the UTC date whereas you may expect the local one. I think it’s always wise to work only with UTC in your domains and convert dates for views only, but you can use the BsonDateTimeOptions attribute to override the default behaviour:

public class Customer
{
	[BsonDateTimeOptions(Kind = DateTimeKind.Local)]
	public DateTime CustomerSince { get; set; }
}

This way the UTC date stored in the document will be converted to the local time zone of the computer.

In case you only want to store the “date” part of a date, i.e. without the hours:minutes:seconds bit then use the DateOnly parameter of BsonDateTimeOptions:

public class Customer
{
	[BsonDateTimeOptions(Kind = DateTimeKind.Local, DateOnly = true)]		
	public DateTime CustomerSince { get; set; }
}

If you remove a property from your POCO then the document will adjust automatically during serialisation as we said before. However, the C# driver won’t like it when deserialising the BSON representation. It will throw a FileFormatException in case a property in the BSON document doesn’t match any of the properties in the POCO. You can make the driver ignore unmapped properties using the BsonIgnoreExtraElements class-level attribute:

[BsonIgnoreExtraElements]
public class Customer
{
	[BsonDateTimeOptions(Kind = DateTimeKind.Local, DateOnly = true)]		
	public DateTime CustomerSince { get; set; }
}

In the next post we’ll start building the POCOs for our demo car rental application.

View the posts related to data storage here.

Advertisements

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

2 Responses to MongoDB in .NET part 3: starting with POCO documents

  1. Pingback: Lindermann's Blog | MongoDB Series

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

ultimatemindsettoday

A great WordPress.com site

iReadable { }

.NET Tips & Tricks

Robin Sedlaczek's Blog

Developer on Microsoft Technologies

HarsH ReaLiTy

A Good Blog is Hard to Find

Softwarearchitektur in der Praxis

Wissenswertes zu Webentwicklung, Domain-Driven Design und Microservices

the software architecture

thoughts, ideas, diagrams,enterprise code, design pattern , solution designs

Technology Talks

on Microsoft technologies, Web, Android and others

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

Anything around ASP.NET MVC,WEB API, WCF, Entity Framework & AngularJS

Cyber Matters

Bite-size insight on Cyber Security for the not too technical.

Guru N Guns's

OneSolution To dOTnET.

Johnny Zraiby

Measuring programming progress by lines of code is like measuring aircraft building progress by weight.

%d bloggers like this: