<CharlieDigital/> Programming, Politics, and uhh…pineapples

5Jul/11Off

The Beauty of Mongo

MongoDB is sexy.  Sexy as hell.

This isn't the first or the best intro to Mongo+.NET, but I hope this one can show you how it finally solves the object-relational disconnect with an easy to follow example.

Let's start with the model:

#region prologue

// Charles Chen

#endregion

using System;
using System.Collections.Generic;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;

namespace MongoAssessmentsTest
{
    public class Assessment
    {
        private DateTime _created;
        private string _description;
        [BsonId]
        private ObjectId _id;
        private List<ContentItem> _items;
        private DateTime _lastUpdated;
        private string _ownerId;
        private List<string> _tags;
        private string _title;

        public ObjectId Id
        {
            get { return _id; }
            set { _id = value; }
        }

        public string OwnerId
        {
            get { return _ownerId; }
            set { _ownerId = value; }
        }

        public string Title
        {
            get { return _title; }
            set { _title = value; }
        }

        public string Description
        {
            get { return _description; }
            set { _description = value; }
        }

        public List<string> Tags
        {
            get { return _tags; }
            set { _tags = value; }
        }

        public DateTime Created
        {
            get { return _created; }
            set { _created = value; }
        }

        public DateTime LastUpdated
        {
            get { return _lastUpdated; }
            set { _lastUpdated = value; }
        }

        public List<ContentItem> Items
        {
            get { return _items; }
            set { _items = value; }
        }
    }

    public class ContentItem
    {
        private List<Question> _questions;
        private string _text;

        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        public List<Question> Questions
        {
            get { return _questions; }
            set { _questions = value; }
        }
    }

    [BsonKnownTypes(typeof(CheckboxQuestion), typeof(RadioQuestion), typeof(SelectQuestion), typeof(FreeTextQuestion))]
    public class Question
    {
        private int _order;
        private int _points;
        private string _text;

        public string Text
        {
            get { return _text; }
            set { _text = value; }
        }

        public int Order
        {
            get { return _order; }
            set { _order = value; }
        }

        public int Points
        {
            get { return _points; }
            set { _points = value; }
        }
    }

    public class CheckboxQuestion : Question
    {
        private List<string> _answers;
        private List<string> _choices;

        public List<string> Choices
        {
            get { return _choices; }
            set { _choices = value; }
        }

        public List<string> Answers
        {
            get { return _answers; }
            set { _answers = value; }
        }
    }

    public class SelectQuestion : Question
    {
        private string _answer;
        private List<string> _choices;

        public List<string> Choices
        {
            get { return _choices; }
            set { _choices = value; }
        }

        public string Answer
        {
            get { return _answer; }
            set { _answer = value; }
        }
    }

    public class RadioQuestion : SelectQuestion { }

    public class FreeTextQuestion : Question
    {
        private List<string> _acceptableAnswers;
        private string _isAnswerCaseSensitive;

        public string IsAnswerCaseSensitive
        {
            get { return _isAnswerCaseSensitive; }
            set { _isAnswerCaseSensitive = value; }
        }

        public List<string> AcceptableAnswers
        {
            get { return _acceptableAnswers; }
            set { _acceptableAnswers = value; }
        }
    }
}

The beauty of it is the absolute simplicity of working with this model.  Aside from a few attributes, there's not much thought that needs to be given to collections and inheritance hierarchies (though there are additional attributes and classes that can be used to control how these are stored if so desired).  I love it because this approach keeps your domain models clean.

How do we interact with this model?

#region prologue

// Charles Chen

#endregion

using System;
using System.Collections.Generic;
using MongoDB.Bson;
using MongoDB.Driver;
using MongoDB.Driver.Builders;

namespace MongoAssessmentsTest
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            Program program = new Program();
            program.Run();
        }

        private void Run()
        {
            MongoServer server = MongoServer.Create(); // Uses default connection options to localhost

            MongoDatabase database = server.GetDatabase("assessments_db");

            if (!database.CollectionExists("assessments"))
            {
                CommandResult result = database.CreateCollection("assessments");

                if (!result.Ok)
                {
                    Console.Out.WriteLine(result.ErrorMessage);
                    return;
                }
            }

            MongoCollection<Assessment> assessments = database.GetCollection<Assessment>("assessments");

            if (assessments.FindOne(Query.EQ("Title", "Sample 1")) == null)
            {
                // Insert an assessment.
                Assessment a = new Assessment
                {
                    Created = DateTime.Now,
                    Description = "A sample assessment",
                    Title = "Sample 1",
                    Items = new List<ContentItem>
                    {
                        new ContentItem
                        {
                            Questions = new List<Question>
                            {
                                new SelectQuestion
                                {
                                    Text = "Who was the first president of the United States?",
                                    Answer = "George Washington",
                                    Choices = new List<string>
                                    {
                                        "George Washington",
                                        "Abraham Lincoln",
                                        "John Adams"
                                    }
                                },
                                new CheckboxQuestion
                                {
                                    Text = "Which of these is NOT a former US President?",
                                    Answers = new List<string>
                                    {
                                        "Benjamin Franklin",
                                        "Hillary Clinton"
                                    },
                                    Choices = new List<string>
                                    {
                                        "Benjamin Franklin",
                                        "Bill Clinton",
                                        "Hillary Clinton",
                                        "Andrew Jackson",
                                        "James Garfield"
                                    }
                                }
                            }
                        }
                    }
                };

                assessments.Insert(a);
            }

            // Get it as BSON - great for writing straight to a web page
            BsonDocument a1 = assessments.FindOneAs<BsonDocument>(Query.EQ("Title", "Sample 1"));

            Console.Out.WriteLine(a1);

            // Get it as an object - great if you want to work with it on the server.
            Assessment a2 = assessments.FindOneAs<Assessment>(Query.EQ("Title", "Sample 1"));

            Console.Out.WriteLine(a2.Title);
        }
    }
}

Brilliantly simple.  Just fire up mongod.exe and you're ready to rock and roll.  Download the example from here: MongoAssessmentsTest.zip

Posted by Charles Chen

Filed under: .Net, Dev, Mongo Comments Off
Comments (0) Trackbacks (0)

Sorry, the comment form is closed at this time.

Trackbacks are disabled.