Channels ▼
RSS

Security

MongoDB with C#: Deep Dive


In the previous article in this series on MongoDB, I explained how to get started with the C# driver in a simple Windows console application. I analyzed several options for generating your own domain classes and mapping them to their underlying MongoDB document-oriented schemata. In this third and final installment in the series, I dive into using the MongoDB C# driver in an ASP.NET MVC Web application. To follow along, you need to understand the concepts introduced in the previous article.

Matching Document Field Names to Domain Class Requirements

Up to this point in the series, I've shown examples of the MongoDB mapping configurations and the operations to apply when you use your own C# domain classes. The documents stored in the collections don't follow the name conventions that are appropriate for the fields in the domain classes, and therefore, it's necessary to tweak mappings. However, one of the great advantages of MongoDB and its C# driver is that you can reduce the required number of mapping configurations to a minimum by following the most appropriate conventions according to the needs of your domain classes.

I want to develop an ASP.NET MVC Web application in C# and I need the properties in each POCO (or Plain Old C# Object) to use Pascal case. Thus, for example, instead of using relase_date as the field name for a document in the games collection, I can just use the same name that I require for the property, ReleaseDate. This way, I can remove the specific mapping configuration for the ReleaseDate property in my domain class and reduce maintenance.

Removing the mapping information with field names for the documents in the collections has a price. If someone changes the name of a property in a domain class while refactoring code, there are going to be undesired side effects in the operations with the related collection. For example, if someone renames the ReleaseDate property to FirstReleaseDate for the Game class, the new insert operations to the games collection performed through the refactored C# application will add a different field to each new document: FirstReleaseDate instead of ReleaseDate. No exceptions will occur during the inserts because each document might have a completely different schema without problems in any MongoDB collection. In addition, when retrieving old documents in the games collection through the refactored C# application, FirstReleaseDate won't be filled up with values from the document. The old documents have the FirstRelease field, which won't be mapped to any property in the refactored domain class.

Another option is to override the conventions that determine the decisions that the MongoDB C# drivers makes when a class is automatically mapped. For example, you can create a new MongoDB.Bson.Serialization.Conventions.ConventionProfile instance, and call its SetIdGeneratorConvention to specify the default mechanism to use for ID generation. Then, you can apply the ConventionProfile to all the classes that belong to a specific namespace. However, overriding the conventions requires you to manually address low-level serialization configuration issues.

So, whenever you decide to reduce mapping information to the minimum by following conventions or overriding them, you must be careful with refactorings that would be safe when not working with automatically mapped domain classes.

Creating a Data Layer for an ASP.NET MVC 4 Web Application

Let's work with a complete example of an ASP.NET MVC 4 Web application that interacts with a MongoDB database and reduces mapping information to the minimum. To keep the example simple, I'll focus on the data layer. I've reduced the necessary code by using the data domain classes as ViewModels. If you already worked with ASP.NET MVC 3 or 4, you know that it is a good idea to have your ViewModels completely decoupled from the data domain classes and types. Thus, we don't consider the Views and ViewModels as pieces of code that follow best practices. In addition, to keep code simple and easy to understand, I haven't included all the necessary exception handling blocks. My main goal here is simply to explain the features of the MongoDB C# driver.

The sample ASP.NET MVC Web application has the following requirements:

  • List the existing games.
  • Create a new game.
  • List the existing players.
  • View the details for a player and his/her registered scores.
  • Create a new player.
  • Delete an existing player.
  • Edit an existing player.
  • Allow a player to see the available games and play one of them.
  • Register a random score when the player decides to play a game.

First, let's create a new ASP.NET MVC 4 Web Application in Visual Studio 2012. I've used RetrogamesWeb for the solution name (see Figure 1).

ASP.NET MVC 4 Web Application in Visual Studio 2012
Figure 1: Creating a new ASP.NET MVC 4 Web application in Visual Studio 2012.

Select the Basic project template in the New ASP.NET MVC 4 Project dialog box, make sure Razor is the value for the View engine dropdown (see Figure 2).

ASP.NET MVC 4 Web Application in Visual Studio 2012
Figure 2: Selecting the Basic template and the Razor view for a new ASP.NET MVC 4 Web Application in Visual Studio 2012.

Now, add a new project to the solution. Select Visual C# | Windows | Class Library in the Add New Project dialog box. I've used RetrogamesWeb.Data for the solution name (see Figure 3).

Adding a C# Class Library to the solution in Visual Studio 2012
Figure 3: Adding a C# Class Library to the solution in Visual Studio 2012.

Next, add the following two references to the new class library, as I explained in the previous article:

  • MongoDB.Bson.dll.
  • MongoDB.Driver.dll.

The RetrogamesWeb.Data project includes interfaces and classes for the entities within the Entities folder. The classes that define the entities include the minimum possible annotations for mappings. As mentioned, the entities will also be used as ViewModels, but the code doesn't include data annotations to avoid confusion. (However, you can create the required ViewModels, add the mapping code, and include the data annotations there.) I explained the IMongoEntity interface and the entities in "MongoDB with C#."

The project includes interfaces and classes that provide repository services for the entities within the Serivces folder. I'll explore these interfaces and classes as I provide their source code. In addition, there is a MongoConnectionHandler class that performs the steps necessary to get a reference to the collection object from the Mongo database object, but in a generic way that all the services can use by following conventions.

The project includes the following folders and files (see Figure 4):

  • Entities folder
    • Entities\Game.cs
    • Entities\Gender.cs
    • Entities\IMongoEntity.cs
    • Entities\MongoEntity.cs
    • Entities\Player.cs
    • Entities\Score.cs
  • Services folder
    • Services\EntityService.cs
    • Services\GameService.cs
    • Services\IEntityService.cs
    • Services\PlayerService.cs
  • MongoConnectionHandler.cs

RetrogamesWeb.Data class library project structure in Solution Explorer
Figure 4: The RetrogamesWeb.Data class library project structure in Solution Explorer.

Create the Entities folder and add a new interface, IMongoEntity:

namespace RetrogamesWeb.Data.Entities
{
    using System;

    using MongoDB.Bson;

    public interface IMongoEntity
    {
        ObjectId Id { get; set; }
    }
}

Now, add the MongoEntity class that implements the previously created IMongoEntity interface within the Entities folder:

namespace RetrogamesWeb.Data.Entities
{
    using MongoDB.Bson;
    using MongoDB.Bson.Serialization.Attributes;

    public class MongoEntity: IMongoEntity
    {
        [BsonId]
        public ObjectId Id { get; set; }
    }
}

Add a new class, Game (that inherits from MongoEntity), within the Entities folder:

namespace RetrogamesWeb.Data.Entities
{
    using System;
    using System.Collections.Generic;
    using MongoDB.Bson.Serialization.Attributes;

    [BsonIgnoreExtraElements]
    public class Game : MongoEntity
    {
        public Game()
        {
            Categories = new List<string>();
        }

        public string Name { get; set; }

        [BsonDateTimeOptions(DateOnly = true)]
        public DateTime ReleaseDate { get; set; }

        public List<string> Categories { get; set; }

        public bool Played { get; set; }
    }
}

Notice that the only attribute specified for the properties is [BsonDateTimeOptions(DateOnly = true)]. This way, the ReleaseDate consists of a date only (the time is always going to be 12:00:00 AM in the field value).

Add a new class, Gender, within the Entities folder:

namespace RetrogamesWeb.Data.Entities
{
    public enum Gender
    {
        Female,
        Male
    }
}

Add a new class, Score, within the Entities folder:

namespace RetrogamesWeb.Data.Entities
{
    using System;

    using MongoDB.Bson;
    using MongoDB.Bson.Serialization.Attributes;

    [BsonIgnoreExtraElements]
    public class Score
    {
        public ObjectId GameId { get; set; }

        public string GameName { get; set; }

        public int ScoreValue { get; set; }

        public DateTime ScoreDateTime { get; set; }
    }
}


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video