Sunday 5 October 2014

C# Simple DAL Layer with MongoDB


Introduction

See Post 1 in the series
http://rundevrun.blogspot.co.uk/2012/11/setup-mongodb-on-windows-8.html




MongoDB is a NoSQL database that stores "documents". The documents are in effect JSON serialised objects. These objects can (and often will) be a concatenation of other object, that is objects with a nested objects in them.

This DAL layer will focus on saving and retrieving C# objects to and from a MongoDB database. The core storage of MongoDB of  Json objects will be ignored as we are only interested C# objects.

Example of Customer with Embedded Address object (which is a collection).

public class Customer
{
public ObjectId Id { get; set; }
public int CustomerId { get; set; }
public string CustomerShortName { get; set; }
public string CustomerLongName { get; set; }
public List<Address> Addresses { get; set; }
public int HomeAddress { get; set; }
public int WorkAddress { get; set; }
public int[] PreviousAddress { get; set; }
}
 
public class Address
{
public int AddressIndex { get; set; }
public string Address1 { get; set; }
public string Address2 { get; set; }
public string Address3 { get; set; }
public string Address4 { get; set; }
public string Address5 { get; set; }
public string Address6 { get; set; }

}
 

Why Create a DAL

Well this could be a long conversation (or rant), but basically you may well want to encapsulate the database access to a single place within your code. Also you may not want the calling layers (you website, logic) to know or care which database is used. It is not uncommon for products to support multiple databases and having a distant layer removes a lot of pain when you want to replace the database used.

In traditional databases (SQL type) we would have to create a table for customer and a table for addresses then link then via database foreign keys or with code.

Creating A Simple DAL (Data Access Layer)

The DAL we will create will be to service the customer class as shown above.
First we will create an empty class and add methods for the dal.

public class MongoDal
{

}

 

Create Single Database 

Create a Database connection that can be used in the whole class. This will return a the same MongoDatabase object with the same parameters. The connectionString and the database should be located within a configuration file, but here are hardcoded for simplicity. This is a private method as we don't want to expose this to calling layer, no point creating methods that can be skipped past.

 
private MongoDatabase Database
{  get
   {
     string connectionString = "mongodb://localhost";
     MongoClient client = new MongoClient(connectionString);
     MongoServer server = client.GetServer();
     MongoDatabase database = server.GetDatabase("test");
     return database;

  }
}
 
 

Create Customer Method

SQL Equivalent :  Insert

Create a method to creates a customer onto the database, note that the get collection is hardcoded to "Customers", this could be supplied to the dal layer at runtime or supplied with configuration file.

public void CreateCustomer(Customer customer)
{   var collection = Database.GetCollection("Customers");
   collection.Insert(customer);
}

 

Get Customer Method

SQL Equivalent : Select

This method will get a customer object with the objectid within the customer class.

1. Define Query
2. Get Collection (customers)
3. FindOne (single) customer
4. Get the Bson document (json)
5. Deserialize back to a Customer object


public Customer GetCustomer(ObjectId objectid)
{  //Query: Get Customer.id == supported objected
  var query = Query<Customer>.EQ(e => e.Id, objectid);  
  var collection = Database.GetCollection("Customers");
  BsonDocument bsonCustomer = collection.FindOne(query);
  Customer customer = BsonSerializer.Deserialize<Customer>(bsonCustomer);
  return customer;
}


This is the core of getting ALL getting all objects from MongoDB. In this example the Query to the database is fixed rather than having a dynamic query supplied by the calling layer. We will touch on this later.

 

Delete a Customer

SQL Equivalent : Delete

You might at some point want to delete a customer from the database, this follows the same core method as getting a customer where a delete query is created and run againist the collection. This is very powerful and needs careful consideration.


public void DeleteCustomer(ObjectId objectId)
{  var query = Query<Customer>.EQ(e => e.Id, objectId);
  var collection = Database.GetCollection("Customers");
  collection.Remove(query);
}
 
 

Get All Customers

SQL Equivalent : Select  *

This will get all customers from the collection. If  you have a very large database with millions of customers this is likely not to make you friends with operations or your boss. But for simple smaller collections it is quite useful.

public List<Customer> GetAllCustomer()
{
  var collection = Database.GetCollection("Customers");
  MongoCursor<BsonDocument> Bsoncustomers = collection.FindAll();
  List<Customer> customers = new List<Customer>();
  foreach (var bsoncustomer in Bsoncustomers 
  {    var cus = BsonSerializer.Deserialize<Customer>(bsoncustomer); 
    customers.Add(cus);
   }


   return customer;
}

You could in the example above use a linq-expression statement in place of the foreach, I've shown this below.

return Bsoncustomers.Select(bsoncustomer => BsonSerializer.Deserialize<Customer>(bsoncustomer)).ToList();

 

A Wee Bit More Advanced

You may have noticed that previous example have a lot code that was very similar. Below we have two methods GetByAddress6 and GetByQuery.

GetByQuery will get a collection of Customers by the query supplied, this query could be anything from getting by name or something more complex.

GetByAddress6 forms a query and passed to GetByQuery to execute it.



public List<Customer> GetByAddress6(string address)
{  IMongoQuery query = Query<Customer>.EQ(x => x.Addresses[0].Address6 == address);
  return GetByQuery(query);
} 
public List<Customer> GetByQuery(IMongoQuery query)
{   var collection = Database.GetCollection("Customers");
  MongoCursor<BsonDocument> Bsoncustomers = collection.Find(query);
  List<Customer> customers = new List<Customer>();
  foreach (var bsoncustomer in Bsoncustomers)
  {     var cus = BsonSerializer.Deserialize<Customer>(bsoncustomer);
    customers.Add(cus);
   }
 return customers;
}


Why do this?

It makes the code a lot more maintainable and reduces the time to add additions to code later and concentrates the same code in the a single place.

 

Using The Code

Example of creating an instance of the dal and adding a customer,

private void Sample()
{
 
Address addy = new Address()
{
 Address1 = "123 High Street",
 Address2 = "North",
 Address3 = "Any Town",
 Address4 = "",
 Address5 = "",
 Address6 = "123 123"

};
 
Customer customer = new Customer();
customer.Id = new ObjectId();
customer.Addresses = new List<Address>();
customer.HomeAddress = 0;
customer.CustomerId = 12345;
customer.CustomerShortName = "Tommy";
customer.CustomerLongName = "Mr Thomous Smith";
 var id = customer.Id;

//Create dal instance
MongoDal dal = new MongoDal();

//Create customer
dal.CreateCustomer(customer);
 
//Get All customers
List<Customer> allcustomer = dal.GetAllCustomer();
 //Get Customer
Customer GetCustomer = dal.GetCustomer(id);

//Delete Customer
dal.DeleteCustomer(id);

 
}

 

Full Code Example

Here's the full thing:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MongoDB.Bson;
using MongoDB.Bson.Serialization;
using MongoDB.Driver;
using MongoDB.Driver.Builders;

namespace SampleCode
{
 
public class MongoDal
{
 
private MongoDatabase Database
{
 get 
 {
   string connectionString = "mongodb://localhost";
   MongoClient client = new MongoClient(connectionString);
   MongoServer server = client.GetServer();
   MongoDatabase database = server.GetDatabase("test"); //name of the database

   return database;
  }
}

 
 
public void CreateCustomer(Customer customer)
{  var collection = Database.GetCollection("Customers");
  collection.Insert(customer);
}

 
public Customer GetCustomer(ObjectId objectid
{
 //Query: Get Customer.id == supported objectid
 IMongoQuery query = Query<Customer>.EQ(e => e.Id, objectid);  
 var collection = Database.GetCollection("Customers");
 BsonDocument bsonCustomer = collection.FindOne(query);
 Customer customer = BsonSerializer.Deserialize<Customer>(bsonCustomer);

 return customer;
 }

 
 
public void DeleteCustomer(ObjectId objectId)
{
 var query = Query<Customer>.EQ(e => e.Id, objectId);
 var collection = Database.GetCollection("Customers");

 collection.Remove(query);

}

 
public List<Customer> GetAllCustomer()
{  var collection = Database.GetCollection("Customers");
  MongoCursor<BsonDocument> Bsoncustomers = collection.FindAll();
  List<Customer> customers = new List<Customer>();
  foreach (var bsoncustomer in Bsoncustomers)
  {    var cus = BsonSerializer.Deserialize<Customer>(bsoncustomer);
    customers.Add(cus);  

  }
}

 
public List<Customer> GetByAddress6(string address)
{ IMongoQuery query = Query<Customer>.EQ(x => x.Addresses[0].Address6 == address);
 return GetByQuery(query);
}

 
public List<Customer> GetByQuery(IMongoQuery query)
{ var collection = Database.GetCollection("Customers");
 MongoCursor<BsonDocument> Bsoncustomers = collection.Find(query);
 List<Customer> customers = new List<Customer>();
 foreach (var bsoncustomer in Bsoncustomers)
 {   var cus = BsonSerializer.Deserialize<Customer>(bsoncustomer);
   customers.Add(cus);  

 }
  return customers;
 }
}
}
  









 



 
 







 

No comments:

Post a Comment

Comments are welcome, but are moderated and may take a wee while before shown.