NoSQL with RavenDB and ASP.NET MVC - Part 1
- by shiju
A while back, I have blogged NoSQL with MongoDB, NoRM and ASP.NET MVC Part 1 and Part 2 on how to use MongoDB with an ASP.NET MVC application. The NoSQL movement is getting big attention and RavenDB is the latest addition to the NoSQL and document database world. RavenDB is an Open Source (with a commercial option) document database for the .NET/Windows platform developed by Ayende Rahien. Raven stores schema-less JSON documents, allow you to define indexes using Linq queries and focus on low latency and high performance. RavenDB is .NET focused document database which comes with a fully functional .NET client API and supports LINQ. RavenDB comes with two components, a server and a client API. RavenDB is a REST based system, so you can write your own HTTP cleint API. As a .NET developer, RavenDB is becoming my favorite document database. Unlike other document databases, RavenDB is supports transactions using System.Transactions. Also it's supports both embedded and server mode of database. You can access RavenDB site at http://ravendb.netA demo App with ASP.NET MVCLet's create a simple demo app with RavenDB and ASP.NET MVC. To work with RavenDB, do the following steps. Go to http://ravendb.net/download and download the latest build.Unzip the downloaded file.Go to the /Server directory and run the RavenDB.exe. This will start the RavenDB server listening on localhost:8080You can change the port of RavenDB by modifying the "Raven/Port" appSetting value in the RavenDB.exe.config file.When running the RavenDB, it will automatically create a database in the /Data directory. You can change the directory name data by modifying "Raven/DataDirt" appSetting value in the RavenDB.exe.config file.RavenDB provides a browser based admin tool. When the Raven server is running, You can be access the browser based admin tool and view and edit documents and index using your browser admin tool. The web admin tool available at http://localhost:8080The below is the some screen shots of web admin tool Working with ASP.NET MVC To working with RavenDB in our demo ASP.NET MVC application, do the following steps Step 1 - Add reference to Raven Cleint API In our ASP.NET MVC application, Add a reference to the Raven.Client.Lightweight.dll from the Client directory. Step 2 - Create DocumentStoreThe document store would be created once per application. Let's create a DocumentStore on application start-up in the Global.asax.cs.
documentStore = new DocumentStore { Url = "http://localhost:8080/" };
documentStore.Initialise();
The above code will create a Raven DB document store and will be listening the server locahost at port 8080 Step 3 - Create DocumentSession on BeginRequest Let's create a DocumentSession on BeginRequest event in the Global.asax.cs. We are using the document session for every unit of work. In our demo app, every HTTP request would be a single Unit of Work (UoW).
BeginRequest += (sender, args) =>
HttpContext.Current.Items[RavenSessionKey] = documentStore.OpenSession();
Step 4 - Destroy the DocumentSession on EndRequest
EndRequest += (o, eventArgs) =>
{
var disposable = HttpContext.Current.Items[RavenSessionKey] as IDisposable;
if (disposable != null)
disposable.Dispose();
};
At the end of HTTP request, we are destroying the DocumentSession object.The below code block shown all the code in the Global.asax.cs
private const string RavenSessionKey = "RavenMVC.Session";
private static DocumentStore documentStore;
protected void Application_Start()
{
//Create a DocumentStore in Application_Start
//DocumentStore should be created once per application and stored as a singleton.
documentStore = new DocumentStore { Url = "http://localhost:8080/" };
documentStore.Initialise();
AreaRegistration.RegisterAllAreas();
RegisterRoutes(RouteTable.Routes);
//DI using Unity 2.0
ConfigureUnity();
}
public MvcApplication()
{
//Create a DocumentSession on BeginRequest
//create a document session for every unit of work
BeginRequest += (sender, args) =>
HttpContext.Current.Items[RavenSessionKey] = documentStore.OpenSession();
//Destroy the DocumentSession on EndRequest
EndRequest += (o, eventArgs) =>
{
var disposable = HttpContext.Current.Items[RavenSessionKey] as IDisposable;
if (disposable != null)
disposable.Dispose();
};
}
//Getting the current DocumentSession
public static IDocumentSession CurrentSession
{
get { return (IDocumentSession)HttpContext.Current.Items[RavenSessionKey]; }
}
We have setup all necessary code in the Global.asax.cs for working with RavenDB. For our demo app, Let’s write a domain class
public class Category
{
public string Id { get; set; }
[Required(ErrorMessage = "Name Required")]
[StringLength(25, ErrorMessage = "Must be less than 25 characters")]
public string Name { get; set;}
public string Description { get; set; } }
We have created simple domain entity Category. Let's create repository class for performing CRUD operations against our domain entity Category.
public interface ICategoryRepository
{
Category Load(string id);
IEnumerable<Category> GetCategories();
void Save(Category category);
void Delete(string id);
}
public class CategoryRepository : ICategoryRepository
{
private IDocumentSession session;
public CategoryRepository()
{
session = MvcApplication.CurrentSession;
}
//Load category based on Id
public Category Load(string id)
{
return session.Load<Category>(id);
}
//Get all categories
public IEnumerable<Category> GetCategories()
{
var categories= session.LuceneQuery<Category>()
.WaitForNonStaleResults()
.ToArray();
return categories;
}
//Insert/Update category
public void Save(Category category)
{
if (string.IsNullOrEmpty(category.Id))
{
//insert new record
session.Store(category);
}
else
{
//edit record
var categoryToEdit = Load(category.Id);
categoryToEdit.Name = category.Name;
categoryToEdit.Description = category.Description;
}
//save the document session
session.SaveChanges();
}
//delete a category
public void Delete(string id)
{
var category = Load(id);
session.Delete<Category>(category);
session.SaveChanges();
}
}
For every CRUD operations, we are taking the current document session object from HttpContext object.
session = MvcApplication.CurrentSession;
We are calling the static method CurrentSession from the Global.asax.cs
public static IDocumentSession CurrentSession
{
get { return (IDocumentSession)HttpContext.Current.Items[RavenSessionKey]; }
}
Retrieve Entities The Load method get the single Category object based on the Id. RavenDB is working based on the REST principles and the Id would be like categories/1. The Id would be created by automatically when a new object is inserted to the document store. The REST uri categories/1 represents a single category object with Id representation of 1.
public Category Load(string id)
{
return session.Load<Category>(id);
}
The GetCategories method returns all the categories calling the session.LuceneQuery method. RavenDB is using a lucen query syntax for querying. I will explain more details about querying and indexing in my future posts.
public IEnumerable<Category> GetCategories()
{
var categories= session.LuceneQuery<Category>()
.WaitForNonStaleResults()
.ToArray();
return categories;
}
Insert/Update entityFor insert/Update a Category entity, we have created Save method in repository class. If the Id property of Category is null, we call Store method of Documentsession for insert a new record. For editing a existing record, we load the Category object and assign the values to the loaded Category object. The session.SaveChanges() will save the changes to document store.
//Insert/Update category
public void Save(Category category)
{
if (string.IsNullOrEmpty(category.Id))
{
//insert new record
session.Store(category);
}
else
{
//edit record
var categoryToEdit = Load(category.Id);
categoryToEdit.Name = category.Name;
categoryToEdit.Description = category.Description;
}
//save the document session
session.SaveChanges();
}
Delete Entity In the Delete method, we call the document session's delete method and call the SaveChanges method to reflect changes in the document store. public void Delete(string id)
{
var category = Load(id);
session.Delete<Category>(category);
session.SaveChanges();
}
Let’s create ASP.NET MVC controller and controller actions for handling CRUD operations for the domain class Category
public class CategoryController : Controller
{
private ICategoryRepository categoyRepository;
//DI enabled constructor
public CategoryController(ICategoryRepository categoyRepository)
{
this.categoyRepository = categoyRepository;
}
public ActionResult Index()
{
var categories = categoyRepository.GetCategories();
if (categories == null)
return RedirectToAction("Create");
return View(categories);
}
[HttpGet]
public ActionResult Edit(string id)
{
var category = categoyRepository.Load(id);
return View("Save",category);
}
// GET: /Category/Create
[HttpGet]
public ActionResult Create()
{
var category = new Category();
return View("Save", category);
}
[HttpPost]
public ActionResult Save(Category category)
{
if (!ModelState.IsValid)
{
return View("Save", category);
}
categoyRepository.Save(category);
return RedirectToAction("Index");
}
[HttpPost]
public ActionResult Delete(string id)
{
categoyRepository.Delete(id);
var categories = categoyRepository.GetCategories();
return PartialView("CategoryList", categories);
}
}
RavenDB is an awesome document database and I hope that it will be the winner in .NET space of document database world. The source code of demo application available at http://ravenmvc.codeplex.com/